
On Thu, 15 Jul 2004 16:03:36 +0300, Peter Dimov <pdimov@mmltd.net> wrote:
Matt Hurd wrote:
Yes, that is the classic solution but it never strikes me as quite what I need.
I've been struggling to find a cute solution I'm happy with. Mainly because I also typically want to expose the foo1_impl() for the cases when I wish to lock externally for multiple operations.
Andrei Alexandrescu's suggestion is to make foo1_impl take a lock:
void foo1() { scoped_lock<> lock(mx_); foo1( lock ); }
void foo1( scoped_lock<> & l ) { assert( l.locked() ); assert( l.mutex() == &mx_ ); // or not, to allow ext. sync
// do things, incl. foo2( l ); }
This doesn't solve the boilerplate problem, though. Maybe something like
void foo1( scoped_lock<> * pl = 0 ) { assert( pl == 0 || pl->locked() ); scoped_lock<> internal_lock( mx_, pl == 0 ); if( pl == 0 ) pl = &internal_lock;
// do things, incl. foo2( pl ); }
where the boilerplate can be hidden in a class.
Been thinking a little about it. I see some pros and cons to this approach. Pro: discrimination at run-time to enable different states to support different locking strategies. Pro: doesn't mess up methods that require bool template parameterisation Con: run-time overhead (or needing better compilers for global optimization than we currently have) Con: defaults on parameters messes up the interface if you want to have other parameters that are optional too. Con: requires a lock abstraction when you might be able to get away without one for your use case in the state your are dealing with. That is, don't need a lock, don't require one. The case for not requiring a lock should have zero cost at runtime. Stroustrup's don't pay for what you don't use principle. The caveat is that with modern compile time programming this should probably translate to something like, "with most modern compilers' optimization capabilities... don't pay...". Maybe a hybrid / dual approach is better. I am still trying to see this in a holistic sense were blocking/polling and non-threaded/threaded can be parameterized as well as allowing dual use of the objects. Would be nice to consider this interface w.r.t. other resource acquisition, such as file and socket i/o as the blocking, try, timed argument is still relevant to these resources as well. Along these lines perhaps locking can be considered one of many potential run-time preconditions or predicates that you may want to pay for a single evaluation/use sometimes to minimize the cost of state transitions. Locking is just one very common example. On the flip side, perhaps we should forget about it or give up ;-) , just layer a simple api on top of the common posix, win32, dce abstractions, like ace does and worry about C++ niceness later. I'd rather get it nice though as there are other libraries that do this already. Regards, Matt Hurd.