Re: [boost] Regressions in your Boost libraries as of 2005-07-19

Douglas Gregor wrote:
You are receiving this report because one or more of the libraries you maintain has regression test failures that are not accounted for. A full version of the report is sent to the Boost developer's mailing list.
Detailed report: http://engineering.meta-comm.com/boost-regression/CVS-HEAD/developer/issues....
There are failures in these libraries you maintain: spirit (1)
|spirit| owi_mt_tests: gcc-3_3-darwin
I do not know how to fix this last remaining problem. I desperately need some help! Thanks, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

On Jul 19, 2005, at 1:20 PM, Joel de Guzman wrote:
Douglas Gregor wrote:
You are receiving this report because one or more of the libraries you maintain has regression test failures that are not accounted for. A full version of the report is sent to the Boost developer's mailing list.
Detailed report:
http://engineering.meta-comm.com/boost-regression/CVS-HEAD/developer/ issues.html
There are failures in these libraries you maintain: spirit (1)
|spirit| owi_mt_tests: gcc-3_3-darwin
I do not know how to fix this last remaining problem. I desperately need some help!
After a few tries I was able to catch it in gdb. Here's a backtrace: #0 0x9004310c in kill () #1 0x9009fb9c in abort () #2 0x001d7c14 in __eprintf () at /usr/include/gcc/darwin/3.3/c++/bits/stl_vector.h:424 #3 0x001c2784 in boost::mutex::~mutex() (this=0x60d40, __in_chrg=2) at /Volumes/scratch/dgregor/BoostRegressionTesting/boost/libs/thread/src/ mutex.cpp:211 #4 0x001c2708 in boost::mutex::~mutex() (this=0x60d40) at /Volumes/scratch/dgregor/BoostRegressionTesting/boost/libs/thread/src/ mutex.cpp:208 #5 0x00003300 in __tcf_1 () at ../boost/spirit/core/non_terminal/impl/object_with_id.ipp:130 #6 0x9002ce38 in exit () #7 0x0000291c in _start (argc=-1610575468, argv=0xa000919c, envp=0x1) at /SourceCache/Csu/Csu-47/crt.c:267 #8 0x8fe1a278 in __dyld__dyld_start () The problem only shows up on my dual-processor G5, and never seemed to happen on single-processor systems, so we have some kind of race condition. Looking more carefully at the Spirit source, I see this: #ifdef BOOST_SPIRIT_THREADSAFE static boost::mutex mutex; boost::mutex::scoped_lock lock(mutex); #endif I don't think that's valid, because two threads could try to initialize the static at the same time. We'd need to do some other kind of locking in here, then create the mutex on the heap. Doug

Douglas Gregor wrote:
The problem only shows up on my dual-processor G5, and never seemed to happen on single-processor systems, so we have some kind of race condition.
[...] Very likely! I noticed similar problems with intel 9 on a hyperthreading machine: http://tinyurl.com/cmq5r Stefan

The problem only shows up on my dual-processor G5, and never seemed to happen on single-processor systems, so we have some kind of race condition. Looking more carefully at the Spirit source, I see this:
#ifdef BOOST_SPIRIT_THREADSAFE static boost::mutex mutex; boost::mutex::scoped_lock lock(mutex); #endif
I don't think that's valid, because two threads could try to initialize the static at the same time. We'd need to do some other kind of locking in here, then create the mutex on the heap.
Right that code absolutely will create a race condition in the mutex initialisation. If it helps any regex has a static_mutex class that is initialised with a static initialiser list (so no race condition), I've been meaning to polish this off as a formal submission, but in the mean time the code is here: boost/regex/pending/static_mutex.hpp. John.

John Maddock wrote:
The problem only shows up on my dual-processor G5, and never seemed to happen on single-processor systems, so we have some kind of race condition. Looking more carefully at the Spirit source, I see this:
#ifdef BOOST_SPIRIT_THREADSAFE static boost::mutex mutex; boost::mutex::scoped_lock lock(mutex); #endif
I don't think that's valid, because two threads could try to initialize the static at the same time. We'd need to do some other kind of locking in here, then create the mutex on the heap.
Right that code absolutely will create a race condition in the mutex initialisation.
If it helps any regex has a static_mutex class that is initialised with a static initialiser list (so no race condition), I've been meaning to polish this off as a formal submission, but in the mean time the code is here: boost/regex/pending/static_mutex.hpp.
Yey! No more spirit regressions. Thanks to Doug, Stefan and the tip from John. Alas, we were not able to use the regex code because it is not header only. So, thanks to Martin Wille who did the fix based on Peter Dimov's suggestion a few months ago. Quoting Martin: A similar problem was lurking in the phoenix closure implementation. I hope both are fixed now. I used the technique Peter Dimov suggested some time ago: X & instance() { static X x; return x; } void f() { call_once( instance ); X /*const*/ & x = instance(); // use x } (My code uses a function-local static boost::call_once instance that gets initialized with a static initializer to guard the construction of the offending mutex.) Of course, the MT code now is even uglier than it used to be before. The tests pass *here*. However, that doesn't imply anything but syntactical correctness, since this is a single-CPU box. Let's await the other results. Affected files: object_with_id.ipp and phoenix/closures.hpp .... Again, thanks to everyone! I learn a lot from you guys! Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

John Maddock wrote:
The problem only shows up on my dual-processor G5, and never seemed to happen on single-processor systems, so we have some kind of race condition. Looking more carefully at the Spirit source, I see this:
#ifdef BOOST_SPIRIT_THREADSAFE static boost::mutex mutex; boost::mutex::scoped_lock lock(mutex); #endif
I don't think that's valid, because two threads could try to initialize the static at the same time. We'd need to do some other kind of locking in here, then create the mutex on the heap.
Doug, thank you very much for tracking that down!
Right that code absolutely will create a race condition in the mutex initialisation.
If it helps any regex has a static_mutex class that is initialised with a static initialiser list (so no race condition), I've been meaning to polish this off as a formal submission, but in the mean time the code is here: boost/regex/pending/static_mutex.hpp.
This looks very nice. However, using static_mutex would create a dependency on Boost.Regex for all Spirit MT code. I hope static_mutex will eventually become a part of Boost.Thread. I changed the offending code to use boost::call_once() for mutex initialization (following a suggestion Peter Dimov made in another thread related to a similar problem). The test passes on OSL2 (darwin-gcc-3.3 toolset) now. Thanks to Doug, John, Stefan and Peter for their support! Regards, m PS: boost::call_once() takes a void(*)(). An overload of call_once that would accept a T(*)() (for T!=void) would be helpful (the value returned would simply get discarded). Send instant messages to your online friends http://au.messenger.yahoo.com

This looks very nice. However, using static_mutex would create a dependency on Boost.Regex for all Spirit MT code. I hope static_mutex will eventually become a part of Boost.Thread.
Good point, I've been meaning to make it header only, but haven't had cause to really up until now.
I changed the offending code to use boost::call_once() for mutex initialization (following a suggestion Peter Dimov made in another thread related to a similar problem). The test passes on OSL2 (darwin-gcc-3.3 toolset) now.
That call_once idea is really cunning. Embarrissingly I only invented static_mutex because I couldn't think of a way of getting call_once to do what I wanted! Just thinking off the top of my head, I wonder if the technique could be boilerplated: template <class Mutex, class id> Mutex& get_static_mutex(); // returns an instance of Mutex that is unique to "id". Extreme Usage Example: ~~~~~~~~~~~~~~~~~ template <class T1, class T2> struct mytag; template <class T1, class T2> void myproceedure(T1 t1, T2 t2) { // get a mutex instance that is unique to this function, *and* the types T1 and T2: boost::mutex m& = get_static_mutex<boost::mutex, mytag<T1,T2> >(); boost::mutex::lock l(m); // thread safe code goes here: // etc.... } Implementation: ~~~~~~~~~~~ All untried: template <class Mut, class Tag> Mut& do_get_mutex() { static Mut m; return m; } template <class Mut, class Tag> void call_once_proc() { do_get_mutex(); } template <class Mutex, class id> Mutex& get_static_mutex() { boost::once_flag once = BOOST_ONCE_INIT; boost::call_once(&call_once_proc<Mutex, id>, once); return do_get_mutex<Mutex, id>(); } How does this look as an extension to Boost.Threads? Unless I've missed something (!) it should work for any mutex type. Actually shouldn't this work for any global variable that you want initialised in a thread safe manner? You know the more I think about this, the more I worry about what I've missed, it can't be this simple can it? Regards, John.

John Maddock wrote:
That call_once idea is really cunning. Embarrissingly I only invented static_mutex because I couldn't think of a way of getting call_once to do what I wanted!
I think your static_mutex would perform better than call_once based code. [...]
All untried:
template <class Mut, class Tag> Mut& do_get_mutex() { static Mut m; return m; }
template <class Mut, class Tag> void call_once_proc() { do_get_mutex(); }
It's this function I wanted to get rid of when I proposed an overload for call_once in the postscript of my message.
template <class Mutex, class id> Mutex& get_static_mutex() { boost::once_flag once = BOOST_ONCE_INIT;
This needs 'static'.
boost::call_once(&call_once_proc<Mutex, id>, once); return do_get_mutex<Mutex, id>(); }
How does this look as an extension to Boost.Threads?
This is very helpful and it likely would get used often. Its only drawback is the need for the unique tag type (well, I guess someone will suggest some macro for that). Unless I've missed
something (!) it should work for any mutex type. Actually shouldn't this work for any global variable that you want initialised in a thread safe manner?
Hmm, shouldn't a general solution become part of a singleton library? Boost.Thread should offer a threadsafe (to initialize) mutex if it can do better than a generic version. This seems to be the case here. Regards, m Send instant messages to your online friends http://au.messenger.yahoo.com
participants (6)
-
Douglas Gregor
-
Joel de Guzman
-
Joel de Guzman
-
John Maddock
-
Martin Wille
-
Stefan Slapeta