
Matt Hurd wrote:
On Fri, 16 Jul 2004 13:54:32 -0400, Michael Glassford <glassfordm@hotmail.com> wrote:
In an attempt to reconcile the different preferences and ideas expressed in this thread, here's a thought experiment for mutex & lock unification. What if the mutex and lock classes were policy-based, allowing users to choose the interface they want and supplying a typedef or two for the "most common" or "approved" variations?
Example below (note that I haven't tried to implement multiple policies elegantly). Hopefully it could be expanded to include recursive, checked, read/write, etc.
<snip>
//Example typedefs:
typedef mutex_type< blocking_mutex_policy, try_mutex_policy, fast_timed_mutex_policy
mutex;
typedef blocking_mutex_type< blocking_mutex_policy, null_policy, null_policy
mutex;
typedef try_mutex_type< try_mutex_policy, null_policy, null_policy,
mutex;
typedef timed_mutex_type< timed_mutex_policy, null_policy, null_policy,
mutex;
<snip>
//Example typedefs:
typedef lock_type< blocking_lock_policy, try_lock_policy, timed_lock_policy
lock;
typedef lock_type< blocking_lock_policy, null_policy, null_policy
blocking_lock;
typedef lock_type< try_lock_policy, null_policy, null_policy> try_lock;
typedef lock_type< timed_lock_policy, null_policy, null_policy
timed_lock;
I think this is thinking along the right track though I'm not fond of your interface above, but it is a good start to the thinking.
Do you mean having three template parameters? I mentioned that that was just for the sake of example. If you mean something else, I'm not sure what.
I've been reading over some of the old boost thread threads and I think I now see why Bill Kempf is now away from boost::thread. The best spin is, I think, he gave up on consensus.
This occurred to me during this massive threading thread. It seems that no two people agree on what a threading library should look like (e.g. some people want one mutex class and one lock class; others want three mutex classes and three lock classes with no overlap; others want three of each, with each being a superset of the last; others like things the way they are now; etc.). I thought perhaps that well designed policy-based classes could give most people what they want without making everyone else have it too.
It it interesting the old ground we are covering, some of the thinking back then was perhaps a little better then. Some of the current ideas seem a little better too.
Yes, many of the same discussions seem to get rehashed, don't they? Much like they do with smart pointers, oddly enough.
I do like David Held's thinking, perhaps because it is similar to mine ;-), in these mails: http://aspn.activestate.com/ASPN/Mail/Message/boost/1213138 http://aspn.activestate.com/ASPN/Mail/Message/boost/1207304
Bill Kempf was concerned about providing as safe as possible semantics for locking. He saw the need for lock transfers especially for Bjarne's auto locking proxy idiom. One way of doing this was to expose lock and unlock on a lock, but he felt exposing the same on the mutex was safer
I read things the opposite way. At the very beginning he wanted no lock an unlock methods on the lock, but changed his mind or at least conceded the argument; however, he thought (and I agree) that lock and unlock methods on the mutex were dangerous. Did you mis-type, or could you give references?
yet still troubling. Bill related transfer semantics to auto_ptr and saw the shared_ptr approach as problematic.
Others are of the opinion that lock and unlock is fine and catering threading newbies should not prevent them from the low level tools that makes their life fun ;-)
I side with Bill, on balance, I think on this. I see public a lock and unlock as the equivalent of a threading "goto".
However, I'm not so sure about his shared pointer reluctance at one level. His argument was that a shared_pointer interface requirements are a lot different to an interface for a lock with move/transfer semantics, which is true. He also argued that sharing a lock made no sense and was dangerous.
I agree that sharing a lock makes no sense; explicit transfer (not sharing) of a locks seems to make sense in some circumstances, though. I think Bill came to the conclusion that transferable locks were something he should do, and that most/all of the people participating in the discussion agreed.
This is kind of true, but we do share a mutex with a shareable mutex (the one i keep calling a shareable mutex and the world calls a read write mutex).
A non-shareable mutex in effect would have a count of zero or one (true or false) and ownership transfer would be the correct interface. Careful consideration must go to thinking about such situations as : do_something( lock_object ); w.r.t. to transfer. Perhaps lock_object = do_something( lock_object ); covers the case, but I can see the danger already...
There are grounds for concern over the implementation delivery of shared / transfer implementation as it should either rely on no-locking, atomic primitives or locking primitives below the normal locking to avoid the chicken and the egg of locking a count.
I'm not sure I understand what you're getting at. To make sure we're talking about the same thing: 1) I'm not talking about transfer of locks between threads. 2) I am talking about transferring locks from one scope to another scope (e.g. out of a function) within the context of a single thread. 3) This is essentially move construction or move assignment (that is, the internal state of one lock--e.g. the mutex reference and lock state--is copied into another lock, then the first lock clears its state so that it doesn't try to unlock the mutex or whatever). It's a transfer of ownership. 4) I'm not talking about transfer = lock promotion or demotion. Though the syntax is the same, it's a different operation (ownership is transferred, but the lock type is changed as well). That's not to say that I disagree with that syntax for promotion or demotion, just that that's not what I mean when I talk about lock transfer.
I would like to see David Held's RIIA mechanism, mixed with Michael's policy approach to deliver a solution that could also be consistent with the resource acquisition requirements of socket and file i/o. Timer interaction, especially because or try_lock, where the timer is a resource should perhaps be covered too.
The mutex taxonomy I have in mind would be: 1. null_mutex elided ops, atomic ops with interlocking
This is a good idea.
2. simple_mutex basic os mutex / critical section recursive on windows, non-recursive on posix 3. recursive_mutex recursive always 4. shareable_mutex supports exclusive and shared access reflects os primitives where available ( what does this mean with recursiveness???) 5. upgradeable_shareable_mutex adds upgradeable read lock equivalent
yeah, maybe not the best names
- a lock is a resource acquisition on a mutex. - avoid lock and unlock methods if possible via move/transfer semantics - consider a read lock as a shared_lock, lots of owners.
run out of time, gotta go bike riding with the kiddies...
$AUD 0.02
Matt Hurd.
Mike