Boost logo

Boost :

From: williamkempf_at_[hidden]
Date: 2001-09-14 14:30:56


--- In boost_at_y..., "Ed Brey" <edbrey_at_y...> wrote:
> From: <williamkempf_at_h...>
>
> > > I agree with you that blocking can be viewed as a side effect
of
> > waiting on a condition. But given this view, the function is
named
> > for its side effect, rather than its primary effect of locking
and
> > unlocking the mutex. In addition, it isn't the condition that
waits,
> > it's this_thread, but waits membership in condition implies
> > otherwise. I see two consistent approaches that can be taken:
(1)
> > keep the function a member of condition and call it
unlock_then_lock,
> > or something less, or arduous, or (2) make it a free function and
> > call it wait. In the latter case, blocking would be considered
the
> > primary effect and unlocking and locking would be considered side
> > effects. Each view is equally valid. The second seems focus
more on
> > the effects that the user cares about.
>
> > You're thinking a little too low level here (and I don't know if
> > thinking about it at the proper level will change your mind, but
we
> > need to get up there to discuss this). The locking and unlocking
are
> > side effects of waiting, not the primary functionality, nor what
> > causes the thread to block (though the locking could extend the
> > length of time that the thread blocks). The actual action is
that of
> > the condition object waiting for a signal that some state has
changed.
>
> I agree completely (except for the first line ;). I also favor
calling function that waits on the condition variable "wait". The
issue is that since it isn't the condition object that does the
waiting, the function should not be a member of condition. The only
reason I mentioned approach 1 above was to demonstrate what the
function would need to be called in order to consistently be a member
of condition. However, we both agree that such a view is too low
level and would obscure the main functionality of the function.

We're going to argue in circles now. I disagree totally about which
object does the waiting. To me it *IS* the condition that does the
waiting.

> > > Certainly not every function that blocks (waits) should be
called
> > wait. Wait is only a good name if waiting is the primary (or
only)
> > effect. For example, istream::open, printf and
semaphore::decrement
> > open, print and decrement, plus block as a side effect, whereas
wait
> > (xtime) just waits, wait(thread) just waits, and wait(condition,
> > lock) waits, plus has a side effect.
> >
> > That's awfully subjective. What's a side effect and what's not?
> > What does mutex.lock() or semaphore.up() do that's not a side
effect
> > thus eliminating them from candidates for wait(mutex) and wait
> > (semaphore)?
>
> Absolutely it's subjective. Fortunately, it also seems clear-cut
enough that a consensous on what is the primary effect is easily
reached. For example, we both agree that when waiting on a
condition, waiting is the primary effect and locking is a side
effect. In the case of mutex::scoped_lock::lock, I'd say that
locking (i.e. guaranteeing exclusive access) is the primary effect,
and blocking is an undesireable but necessary side effect needed to
accoplish it. For semaphore, both the waiting and signalling are
important, but perhaps we can all agree on the reasonable view that
incrementing and decrementing are the primary effects, and blocking
is a side effect. What do you think?

No, it's not clear-cut at all, and a consensus is no where near to
being reached. Waiting is the primary effect, yes, but you're making
this synonymous with the thread blocking, which is as much a side
effect as the locking/unlocking of the mutex. The operation is to
wait for the condition to be notified, the thread blocking is solely
a side effect of this operation. The action is taking place on the
condition, not on the thread. The thread only reacts from a side
effect of the operation.

> > Actually, join() does more than wait for the thread to finish.
It
> > also cleans up all resources associated with that thread. It's
akin
> > to fstream::close() in this regard. But this is a different
argument
> > from the one in this current discussion.
>
> I'm confused by this. Your documentation for join says that the
only effect is to block.

No, it says more than this. It says that all resources are
reclaimed. Reading the current documentation I see that the wording
isn't specific enough to make clear what this means and I should work
on it. One of the resources that's reclaimed is the "thread id".
This means that after a call to thread::join() all other operations
are invalid on the thread object, the same as attempting to read or
write to/from an fstream that's been closed.

>The reference to resources being reclaimed speaks only to how long
the blocking lasts. This seems quite different that fstream::close
(), where the file wouldn't be closed had the function not been
called. With threads, the thread will terminate and be cleaned up
whether join is called or not (according to my understanding of your
documentation). Is there something I'm misunderstanding?

Yes, and what you're missing has been discussed on here so I won't
rehash it any more than I already have, but this discussion has
pointed out that the current docu isn't explicit enough. I'll work
on that.
 
> > To me this just further shows how this concept is too ambiguous.
> > Given the name "wait" you and I have completely different notions
of
> > what to expect. Given the name "sleep" we both have a clear
> > understanding of what to expect.
>
> For discussions like this, terms like sleep and join are great
because they concisely separate different actions. It's just like if
we were to discuss the various overloads of vector::insert, we may
wish to have concise terms to discuss insert range, insert element,
and insert copies of element. But once the discussion is done, those
terms turn into baggage because the scope changes from focusing on a
small area of a library to writing an entire program using many
libraries, and each new name and concept has a ramp-up cost.
Therefore, if we can bundle like concepts into a group, but make
their actions clear and distinct by overloading, we've made the
library more usable. I think that there is a good potential for this
in the thread library, and when taken as part of a consistent
interface, no one will get confused if they see wait(timeout), and
think it is going to return immediately. The value gained is that
rather than presenting a series of fairly unrelated functions, we can
present the "wait family of functions", all of which have waiting as
a primary effect and all of which have a common convention for
specifying (or not) a timeout.

The Win32 model, which I've explained why I think it should be
avoided. Most (not all if you include sleep, which is yet another
reason I wouldn't care to do this) of these operations have a
converse operation. If you lump one side into a single name, wait,
I'd expect you to lump the other side into a single name, and that's
where things break down. The name "wait" only *appears* to be valid
because of the side effect of the thread blocking.

Bill Kempf


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