Good day,
I believe there's a problem with the use of static local variables
w/non-trivial constructors within log4cxx code (bug LOG4CXX-159,
http://issues.apache.org/jira/browse/LOGCXX-159 ). Log4cxx contains things like
this (from ndc.cpp):
const LogString& NDC::getNull() {
static LogString nullStr(LOG4CXX_STR("null"));
return nullStr;
}
I think there are two major problems with this (and similar) code:
1) I think it is not thread safe. Local statics are initialized the first time
the enclosing function is called (C++'98 ISO standard, section 6.7). If two
threads call it at about the same time from different threads, they may both
try to initialize the static local at once, effectively executing the object's
constructor on the same object simultaneously. [I vaguely remember reading
something about Visual C++ generating code that ensures this doesn't happen,
using mutexes or something... even if it's true, other compilers most likely
wouldn't do that].
2) The order of construction and (especially) destruction of such local statics
is ... "interesting", esp. w.r.t. static objects defined in other object files.
LOG4CXX-159 contains a couple of example programs that demonstrate this, but
the bottom line is, such local static variables are sometimes destructed before
static objects in user code are, which in my case caused SEGFAULTs.
I understand static locals were introduced in favor of globals to make sure
they are initialized as necessary... however there are still instances where
global statics are used (Level::ALL, etc); are there any plans to do anything
about them?
Also, as suggested by Curt in that JIRA discussion, arranging a call to
something like "Logger::getLogger(...)->forcedLog(...)" before anything else
happens in user code (i.e., from some static ctor), seems to exercise most of
the static local initialization, but this doesn't always work (I can give
specific examples). Plus one needs to configure() log4cxx before doing that.
Anyway, a possible solution that ensures *all* log4cxx statics are initialized
before anything the user code might possibly do could be this: convert all
statics (both local and global) to [static] global pointers and
initialize/uninitialize (new/delete) them explicitely in an additional global
init/uninit function. Then, add a call to init() at the global level inside one
of log4cxx headers that is always #include'd in the user code (config.h?); that
way init() will be called before any user constructors.
Unfortunately this will probably require some changes to the public API --
since we can't use true statics, public static class member variables would
have to be changed to functions that return references.
Thoughts, comments?
D.