Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2001-09-22 14:31:00


From: "David Abrahams" <david.abrahams_at_[hidden]>
> From: "Peter Dimov" <pdimov_at_[hidden]>
>
> > > As counterintuitive as I find this way of thinking, I'm willing to
> assume
> > > you understand threads at a deeper level than I do, and when I get to
> your
> > > level I'll see that it all makes sense. So, if we make that
assumption,
> I
> > > would want to see something more like condition::get() than
> > > condition::wait(). The condition doesn't wait any more than a blocking
> > read
> > > from a file causes the file to wait. I don't really see any difference
> > > between blocking and waiting, but if you insist that there is a
> > difference,
> > > it seems like we should abolish the term "wait". If anything, I think
it
> > > will confuse people.
> >
> > The only consistent approach to naming is to adopt POSIX names AS-IS.
The
> > POSIX committee has already done the arguing for us.
> >
> > This means thread::join, condition::{wait, signal, broadcast.}
>
> Well, OK, but POSIX doesn't have member functions (or overloading) to work
> with. When you write x.f() most of us think that "x is performing action
> f()", and (overgeneralizing broadly here) nobody except thread library
> implementors will find it natural to think of a "condition" performing an
> action, especially one called "wait". A condition (speaking English here)
is
> a passive thing which arises out of the circumstances.

I agree with everything you say here; I was responding to your original
suggestion that we should "abolish the term 'wait'."

POSIX specifically talks about "waiting on a condition." Yes, it's not the
condition that waits; the 'performer' is not the condition variable, but the
action is still 'wait.'

Aside: I, personally, don't read into the x.f() syntax more than it's
necessary, meaning that I see in x.f() a function call with x as a first
argument, implicit friend to X, and no conversions. ;-)

> > std::for_each(first, last, thread::join);
> >
> > std::for_each(first, last, (void (*) (thread &))thread::wait);
>
> There are lots of ways around that problem, like defining forwading
> functions, or using member functions on thread and condition (I have no
> objection to implementing the global wait(condition) in terms of
> condition.wait()).

There are lots of ways around every problem, even when the technical problem
is an indication of a design problem.

The point is that overloading a name to mean different things in different
contexts is not necessarily a good thing. An overloaded name implies that I
don't care about the specifics of the call. I simply say x + y and expect it
to do the right thing. Obviously, when I say wait(x) I do care about which
'wait' variation is being performed. The 'join' variation invalidates x; the
'sleep' variation doesn't modify x; the pthread_cond_wait variation has (at
least) an additional mutex argument that it implicitly releases and
acquires.

In short, the semantics of wait(x, ...) are radically different. Given the
template

template<class T> void f(T & t);

I can't simply say wait(t) inside and trust it to perform the right kind of
operation. I have to know what t is, and if I do know that, why define the
function as a template?

(The Win32 API is a good example of why this kind of wait() overloading
doesn't really work.)

Now if you refine the wait() semantics a little, saying that wait(e) blocks
the current thread until the event e occurs, and then provide a way to
specify a 'thread finished' event, a 'time reached' event, a 'predicate is
true' event, I'll be inclined to agree with you. A variation would be the
familiar wait(event, timeout); or you could go further and define expression
templates for the events supporting && and ||. ;-)

--
Peter Dimov
Multi Media Ltd.

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