
Hi Cory, Thanks for your feedback. "Cory Nelson" <phrosty@gmail.com> writes:
On 10/5/06, Anthony Williams <anthony_w.geo@yahoo.com> wrote:
I have revamped the code on the threads_rewrite branch for the mutex and read_write_mutex.
The read_write_mutex code is based on ideas from the code offered by Peter Dimov and Cory Nelson. However, I have extended it to include upgradeable locks, and more closely mirror the interface proposed in the C++ Standard threading proposal N2094. At the moment it does not support try_ or timed_ variants. The code can be seen in CVS at boost/thread/win32/read_write_mutex.hpp on the thread_rewrite branch, or in the CVS web viewer at
In all cases, the code tries to change state using compare-and-swap instructions. If the current state is blocking the state change (e.g. there is already a writer when we try to lock a reader), then the lock blocks on one of three win32 Event objects. The shared event is a manual reset event, so once readers are allowed access, they are all freed until a thread obtains an exclusive lock and resets the event. There is a limit on how many threads can have a shared/upgradeable lock (0x1fff), and how many can be waiting for a write lock (0xfff). Given the nature of read-write mutexes, this could easily be adjusted to allow more shared locks, and fewer waiting writers.
I've just looked the reader-writer lock over, and while it generally seems "usable" to me, I've got a few nitpicks:
1) This seems like a real big general-purpose class that goes against the C++ism of not paying for what you aren't going to use. Creating three events (these are kernel objects) for each lock makes it a rather heavy instance - I feel like using of a lot of them for fine-grained locking won't be realistic. Given that the need to upgrade a lock is uncommon and usually easily worked around, and the ease a user can cause deadlock when upgrading, would you consider a separate lightweight non-upgradeable class to go alongside it?
I would certainly think it worth having multiple mutexes supporting various levels of locking. Howard's proposal (N2094, see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2094.html) has 4 levels --- simple mutex, mutex with shared/exclusive locks, mutex with shared/exclusive locks where the shared ones can "try" to convert to an exclusive lock, and mutex with shared/exclusive/upgradeable locks. This is an implementation of the latter, since I deemed it the hardest to get right.
2) It doesn't spin at all - giving an app a chance to stay away from WaitForSingleObject on multithreaded systems will be a good boost to scalability under typical use. Win32 critical sections have a default spin count of 4000 on multithreaded systems. With multi-core getting more and more common I think this is an important aspect to consider.
Yes, it's worth considering. I would be intrigued to see what difference it made. I have a dual-core system and a single-core system, so I could see how it performed on both with/without spinning. Any ideas for how to construct a benchmark?
3) I don't like the lock(), unlock() etc member functions being public.. forcing scoped_lock is a great cheap idiom for exception safety and saving people from deadlocks. Even with scoped_lock also available - it isn't something found in other languages so I fear new developers will flock to using the simpler and more familiar public members instead.
Howard's proposal has them as public members, so that's what I implemented. I can see the arguments both ways. Making the functions private is easy, and what boost does already. There are certainly use cases where pure scoped locks get in the way. Anthony -- Anthony Williams Software Developer Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk