Boost logo

Boost :

From: Ed Brey (edbrey_at_[hidden])
Date: 2001-09-14 10:05:51


From: <williamkempf_at_[hidden]>

> > 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.

> > 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?

> 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. 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?

> 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.


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