Boost logo

Boost :

From: Jonathan Biggar (jon_at_[hidden])
Date: 2002-10-16 19:45:29


David Abrahams wrote:
> > > b. What is the rationale for condition::wait() throwing lock_error
> > > if !lock.locked()? Would it not be cleaner to simply lock the lock
> > > if it arrives in an unlocked condition?
> >
> > It is almost certainly an error to call condition::wait() without having
> > the lock, since the application code is responsible for testing the
> > application data that the condition and lock protect. If you aren't
> > holding the lock, you either have a race condition or a deadlock
> > lurking.
>
> I also realize that, given the way condition::wait() is
> implemented. Did you read my 2nd sentence? If I don't have the lock,
> and I want to wait on the condition, why shouldn't it just acquire the
> lock for me? Use case: the condition protects some input queue. I want
> to block until some data is available in the queue. I don't need to
> touch the queue until there's data ready, so I have no reason to lock
> the mutex ahead of time other than that's what the condition interface
> asks me to do.

That use case will fail as described. You must test the queue to see if
it already has data in it before calling wait or you might never get
woken up, and that test must be done with the lock held to avoid a race.

I've never seen a workable use case for calling wait() on a condition
without performing some test on the data that the condition is
protectiong, and that means the lock has to be acquired first. That's
the basic design pattern for using locks & condition variables:

    lock->acquire();
    while (!predicate())
        cond.wait();
    // modify protected data here
    lock->release();

Of course to be exception safe, you use a lock guard rather than just
acquiring and releasing the lock.

> It's always more pleasant to use an interface which "always works"
> than one which throws at you to let you know it doesn't like the state
> of your inputs.

It would be nice, but that's not the way to make this "always work".

> > > d. If the predicate form of wait(lock, pred) were implemented the
> > > way it's specified, there would be a race condition:
> > >
> > > while (!pred()) wait(lock);
> > >
> > > Since the predicate is checked before the non-predicate form checks
> > > for the lock being locked and throws an exception. Fortunately,
> > > your implementation doesn't work that way.
> >
> > This is only a race condition if the code violates the pre-condition
> > that the lock must be locked before testing the predicate and calling
> > wait().
>
> It's not a precondition. The behavior in case the lock is not lock is
> well-defined by the documentation, and lock.locked() is not in the
> Requires: clause. It doesn't make sense to specify behavior in the
> case of precondition violations.

It is a precondition in an informal sense, in that wait() throws rather
than doing what it is expected to do.

> > > However, I think I like:
> > >
> > > do { wait(lock) } while(!pred());
> > >
> > > better.
> >
> > This deadlocks if the predicate is already true before the loop is
> > entered!
>
> I think you're right. Oops.
>
> > > e. misuse of the term `model'. Types model Concepts, objects do
> > > not. You should just be saying "mutex object", not "mutex model".
> > >
> > > f. The non-predicate form of timed_wait Effects: clause ends with
> > > "...and then reacquires the lock". This appears to mean the lock is
> > > reacquired regardless of whether timeout arrives before
> > > notification. I think that can't be intentional. Can it?
> >
> > Yes, that is intentional. The required condition variable pre- and
> > post-condition logic for wait() is that the associated lock is locked.
> > Without that, you get race conditions.
>
> Only if you access the associated data, right? Well, it wasn't
> completely clear to me that that was supposed to be allowed in the
> case of timeout. It means that even with a timeout, you can't come
> back from wait() if some other thread has the mutex locked. I think it
> would be good to spell that out somewhere in the condition docs.

Right, but you have to access the associated data to see if you are
going to call cond.timed_wait() in the first place.

-- 
Jon Biggar
Floorboard Software
jon_at_[hidden]
jon_at_[hidden]

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