opensubscriber
   Find in this group all groups
 
Unknown more information…

l : log4cxx-dev@logging.apache.org 20 January 2009 • 9:06AM -0500

Re: Crashes on exit() from multithreaded program using log4cxx
by rhosyn@purplescarab.com

REPLY TO AUTHOR
 
REPLY TO GROUP






Curt Arnold wrote:
>
> On Jan 16, 2009, at 9:34 AM, Rhosyn wrote:
>
>> Dear all,
>>
>>
>> The crashes
>> ===========
>>
>> We would like to share with you some patches to log4cxx  we have found
>> useful in our environment.
>>
>> Our product  (a Linux platform server product, compiled with g++ 4.3.x)
>> started suffering from crashes during (what should have been) graceful
>> process exit ("exit(0);")- after we replaced our old logging framework
>> with a log4cxx backend.
>>
>> Commonly we would see segmentation faults in:
>>
>> * log4cxx::helpers::ObjectPtrBase()
>> * log4cxx::LogManager::getLoggerLS()
>>
>>
>>
>> The cause
>> =========
>>
>> We eventually traced this issue to the pervasive pattern of (mis)usage
>> of singletons in the log4cxx code.
>>
>> The log4cxx library uses the "Meyers" singleton pattern, first
>> popularised in Scott Meyers "Effective C++" (Item 47 in the 2nd edition
>> of that book):
>>
>> Thing & getThingSingleton()
>> {
>>    static Thing t;
>>    return t;
>> }
>>
>>
>> For many years, the above pattern was considered "best practice" for
>> using Singletons in C++ - and was generally safe for most popular
>> compiler implementations and most applications.
>>
>> Unfortunately, this recommendation is not actually guaranteed to be
>> thread-safe for construction or destruction - something which is alluded
>> to on Scott Meyers' own "Errata List for Effective C++, Second Edition"
>> as described here: http://www.aristeia.com/BookErrata/ec++2e-errata.html
>>
>>
>> The nub of the problem is that when a process calls "exit(0);" or
>> similar, one thread will start running, in order, any user-registered
>> "atexit" functions.
>>
>> Along with these, the compiler will execute the (conceptually similar)
>> compiler-registered functions which invoke the destructors of any static
>> file or function scope objects (also in order - the reverse order to
>> static object construction).
>>
>> Unfortunately, other threads may still be in the process of running -and
>> logging, perhaps using the static objects - during or after the
>> execution of their destructors - thus opening the door to a bunch of
>> potential SEGFAULTs.
>>
>>
>> Solutions
>> =========
>>
>> Andrei Alexandrescu goes into a lot of detail about this C++ design
>> problem in chapter 6 of "Modern C++ design" and proposes two elegant
>> solutions -  a "Phoenix Singleton" or  SingletonHolder class.
>>
>> The patches attached are somewhat less elegant than either of
>> Alexandrescus suggestions (we were pressed for time and needed a quick
>> fix in order to ship on time).
>>
>> However, the patches supplied are simple, pragmatic - and did appear to
>> hold up during our testing (testing which was readily producing the
>> crashes described earlier, before we patched).
>>
>> In summary, the patches change Singleton functions to work thus:
>>
>> Thing & getThingSingleton()
>> {
>>    static Thing * t = new t;
>>    return *t;
>> }
>>
>> Of course, the downside of this flavour of fix is that:
>>
>> * the static objects - now allocated on the heap with new() - never get
>> their destructors run.  AFAIK, no other resources (other than memory)
>> appear to be leaked (due to this patch).  Fortunately, as we are running
>> on a modern OS, we are able to rely on the OS to reclaim the process
>> memory on process exit - thus nullifying this particular issue for our
>> product (but not neccessarily so in other environments).
>>
>> * The patch doesn't address the startup/initialisation race  (for that
>> we'd need a multiple-locked singleton initialization pattern everywhere
>> log4cxx creates a singleton) - we're less worried about that at this
>> stage as we have yet to notice any issues.
>>
>> apr
>> ===
>>
>> Finally we couldn't work out how it could ever be safe to deiinitialise
>> APR if there was even the slightest chance that any extant log4cxx
>> objects existed (accessible to any thread).  We therefore removed the
>> apr_terminate() call in APRInitializer::~APRInitializer()
>>
>>
>> Future
>> ======
>>
>> I would like to respecfully suggest that there is a discussion in the
>> log4cxx community about  the best way of reworking the use of singletons
>> in the log4cxx library  (multithreaded-safe construction and
>> destruction)  - and that we look to moving towards a different pattern
>> of usage.
>>
>> I suspect Alexandrescu's "singleton holder" idea might form a part of a
>> possible solution - but it's not the only game in town.
>>
>>
>>
>
> Thanks for the analysis.
>
> There is a bit of tension here since those who are running under
> BoundsChecker, Valgrind, Purify et al will then complain about leaks.  
> Probably the best approach is to try to isolate the singleton pattern
> into a preprocessor macro and then allow the user to select what
> singleton pattern they'd like to use.
>
> Please file this as a bug report at http://issues.apache.org/jira
>


Thanks; JIRA issue LOGCXX-322 raised, as requested.


I totally see your point re: BoundsChecker, Valgrind etc. and also re:
isolating the behaviour.

A more sophisticated patch than my first cut is definitely the order of
the day!


Bookmark with:

Delicious   Digg   reddit   Facebook   StumbleUpon

Related Messages

opensubscriber is not affiliated with the authors of this message nor responsible for its content.