
On Sep 9, 2005, at 7:34 AM, John Maddock wrote:
template <class TryLock1, class TryLock2> void lock(TryLock1& l1, TryLock2& l2);
and maybe also:
template <class TryLock1, class TryLock2, class TryLock3> void lock(TryLock1& l1, TryLock2& l2, TryLock3& l3);
Yep, those would be useful additions to Boost.Thread IMO.
Problem: With the current interface, my "generic" lock algorithm is no longer generic. It has to know about read_lock() and write_lock(), even though these concepts have absolutely nothing to do with how you lock two locks while avoiding deadlock.
Ah, that's what happens from looking at my not so good test case, rather than the docs :-)
try_read_write_mutex does have member typedefs try_read_lock and try_write_lock, which do what you want them to, I just happened to use a different locking primitive because it was easier to change the test code around to test different combinations of readers and writers.
Sorry, but: Houston, we have an even bigger problem...
Not this time, still it was big enough to begin with :-(
As a simple exercise, code this: template <class TryLock1, class TryLock2> void lock(TryLock1& l1, TryLock2& l2); Here is one possible implementation: template <class TryLock1, class TryLock2> void lock(TryLock1& l1, TryLock2& l2) { while (true) { l1.lock(); if (l2.try_lock()) break; l1.unlock(); l2.lock(); if (l1.try_lock()) break; l2.unlock(); } } Another possibility would be to assume an ordering functionality among the locks: template <class Lock1, class Lock2> void lock(Lock1& l1, Lock2& l2) { if (l1 < l2) { l1.lock(); l2.lock(); } else { l2.lock(); l1.lock(); } } The former assumes the locks know how to lock(), try_lock() and unlock(). The latter assumes the locks are less-than-comparable, and know about lock(). Neither of these assume try_read_lock or try_write_lock. Read locking and write locking are issues orthogonal to this generic code. If this generic 2-lock function must know how to read-lock and write-lock (and perhaps even promote from read to write lock), things get significantly messier. In the design I've laid out, the generic 2-lock algorithm can deal with two exclusive locks, two read locks, two upgradable locks, or any combination of the above (modulo the ordering functionality -- that's a different soap box ;-) ). You could even atomically lock an exclusive lock, while promoting another one from read to write using this same generic code (using a promote_lock adaptor not yet presented). And if you don't do something like the above, you are subject to deadlock if a thread tries to hold one lock while promoting another. A lock (read, scoped, whatever) is either locked or unlocked. You can try-lock it or timed-lock it. But you can't read-lock it, write-lock it or any other kind of lock. You can only lock it or unlock it. It is the mutex that holds the read/write magic, not the lock: template <class RW_Mutex> class sharable_lock { public: ... void lock() {m_.lock_sharable(); locked_ = true;} ... private: mutex_type& m_; bool locked_; }; -Howard