Boost logo

Boost :

From: William Kempf (sirwillard_at_[hidden])
Date: 2000-08-23 09:23:39


--- In boost_at_[hidden], Jens Maurer <Jens.Maurer_at_g...> wrote:
> I think that no amount of library or language wrapping that does not
> cripple usefulness can avoid that the programmer has to understand
> what's going on, and that means getting acquainted with the
complexity.
> This is unlike std::map, where the user doesn't have to understand
> red-black trees (I don't) to use them correctly (I hope I do
> sometimes).

Hear, hear! No one has said so, but I've gotten the feeling that
some think there is some "silver bullet" that will bring concurrent
programming to the masses. We can, and should, make a library that's
as safe as humanly possible, and strive for high level abstractions
that are easier to use, but the bottom line is that this is still
going to be error prone and dangerous in the wrong hands.
 
> I think that striving for compile-time support for error detection
> is a good idea in general (that's why we're using a statically-typed
> language), but not very applicable to the problems with threading,
> which are run-time dynamic in their most intrinsic nature.

There are places where compile time support can be used, such as
passing a lock instead of a mutex to the wait() of a CV. In other
places runtime checking can help diagnose errors, at least during
debug builds.
 
> We should, of course, help the programmer, but I think we help him
> most by describing safe design patterns, not crippling natural
> (common) expressiveness and providing adequate library support for
> that.

Hear, hear!
 
> In particular:
> - Don't force the use of lambda expressions if the alternative
> is a simple while(...) loop pattern to be explained

Lambda expressions are complex and many programmers don't understand
them. For that reason I might agree that we should provide both
alternatives of wait(), those with lambda expressions and those with
out. If it weren't for the fact that some compilers don't support
lambda expressions well and many programmers don't understand them,
I'd disagree. This is one "feature" that could reduce a great number
of programming errors with out sacrificing any expressiveness of
design, or adding any unnecessary overhead.

> - Don't put too much effort in the operator->() auto-lock trick.
> That's basically equivalent to the Java "synchronized" keyword
> attached to each method, which has likely an inadequate lock level.

I mostly agree. At some point in the future it might be worth
providing a LockPointer since it is a useful technique for some cases
(your point about the Java "syncrhonized" fails to state that though
it has problems, it does work for a good number of problems in
Java). However, the issue about the lock class brought up by the
discussion of this LockPointer is something worth addressing even at
this point.

> - Allow for simple implementations: If the user doesn't need
> a recursive mutex, don't force one on him (thus forcing the
> underlying system-specific implementation to possibly craft one
> together expensively). If the user only needs a critical section,
> don't force a monitor on him.

I'm on the fence about the recursive mutex. Implementing a recursive
mutex over a non-recursive mutex isn't that complex or expensive. In
fact, I had to do this in the Win32 implementation in order to allow
for CVs on the recursive mutex. So I don't necessarily think that
performance is an issue here. What is an issue is that non-recursive
mutexes used in OO programs often lead to deadlock. Complex idioms
have to be used when you have non-recursive mutexes, such as having
every public method simply be a wrapper around a call to a hidden
method, where the public methods do the locking. (I hope I've
explained that well enough... Douglas Schmidt has an article on this
design pattern, but I don't have the URL handy.)

Other than that, though, I fully agree with your main point here.

> - Implement generic debugging aids, if possible. For example, a
> mutex deadlock detection is a must.

Yep. Though I'm not the one to tell you how this can be done ;).

> - Don't put arbitrary limits on the number of condition variables
> attached to one mutex. The buffer example shows that several of
> those are useful.

Yep.

> - Hoare's monitor pattern says that the mutex must be locked
> around notify() to avoid race conditions. Even though pthreads
> doesn't seem to require this, it's still a good idea to follow the
> textbook pattern, I think.

Is that why the book I have on pthreads says that you must do this?
Exactly where in Hoare's monitor pattern does it say this? What's
the quote? I seem to have missed it.

> - Start thinking about higher-level synchronization abstractions
> between threads, for example type-safe (message) queues, and think
> about what medium-level options for their implementation should
> exist (mutex, monitor, semaphore, etc.).

Thinking about and discussing are two very different things. I don't
think we should discuss them to any extent until after the low level
primitives exist, at least to some degree. We have the Mutex,
Semaphore and ConditionVariable defined enough to move on for now, I
think. Next we need AtomicCounter and ThreadLocalStorage. At that
point we'll have enough to allow library writers to write thread safe
libraries. After that we can move on to Thread, to allow programmers
to actually write threaded programs. From there we can move on to
the other abstractions, such as MessageQueue and ThreadPool.

> - Provide a really broad choice of options (recursive vs. non-
> recursive mutex, read/write mutex, atomic counters, several
> types of monitor), have a trial phase where users implement their
> favorite abstract data types / application / whatever with these and
> then remove the obviously unused ones.

Agreed, though I still don't think Monitor is a type, but instead
must remain just a pattern.

> - Write down the patterns you're trying to introduce. Nowadays,
> someone writes "singleton" in a comment at the top of some class
> and I can basically skip all the details because I know how it
> should work. The goal here is exactly the same: Someone writes
> "monitor" on top of some class and the reader should know what's
> going on.

Agreed.
 
> C++ can effectively support some design patterns with its advanced
> features such as templates (e.g., singleton) so that a detailed
> description becomes almost superfluous. I don't have the impression
> that concurrent programming patterns are of that nature.

That's *precisely* why I don't think Monitor should be a type! It's
a wonderful pattern, but making it a type will simply limit it's
expressiveness.

Bill Kempf


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk