Boost logo

Boost :

Subject: Re: [boost] [threads] API Design Question
From: Alexander Bernauer (alex_at_[hidden])
Date: 2009-02-06 05:34:36


On Thursday 05 February 2009 15:25:50 Phil Endecott wrote:
> if you notify with the lock held the
> waiting thread may be scheduled but it will immediately block trying to
> re-acquire the mutex, and the first thread will be scheduled again to
> release it.

I checked the posix documentation and indeed it is not guaranteed that
the above does not happen. I didn't expect that.

> I haven't measured the effect this though.

Maybe some implementations are smart enough to avoid this scenario. At
least I hope so.

> Can you give an example?

In my case, I have two event queues. One for immediate events and one
for delayed ones, i.e. something like
std::queue<Event*> imQ;
std::priority_queue<std::pair<system_time, Event*> > delQ;

Both queues are encapsulated by a "meta"-queue class providing
void push(Event*, time_duration delay=not_a_date_time) and
Event* pop()

Besides normal queue semantics the meta queue has the additional
constraint that a delayed event may not be returned by pop() before its
delivery time has been reached.

Without spurious wakeups, we would have
---8<---
void push(Event* event, time_duration delay=not_a_date_time)
{
   lock l(mutex);
   if (delay == not_a_date_time) {
      imQ.push(event);
   } else {
      delQ.push(std::make_pair(get_system_time() + delay, event));
   }
   
   condition.notify();
}
--->8---
and
---8<---
Event* pop()
{
    lock l(mutex);
    while(1) {
        if (not imQ.emtpy()) {
           Event* event = imQ.top(); imQ.pop(); return event;
        } else {
           if (not delQ.empty()) {
               const system_time now = get_system_time();
               if (delQ.top().first < now) {
                  Event* event = delQ.top().second; delQ.pop(); return event;
               } else {
                  condition.timed_wait(l, delQ.top().first); // (1)
               }
           } else {
              condition.wait(l); // (2)
           }
        }
    }
}
--->8---

While writing those lines I realized that the above code is correct even
if there are spurious wakeups. My point was that (1) becomes
---8<---
while (imQ.empty()
   and get_system_time() < delQ.top().first
   and condition.timed_wait(l, delQ.top().first));
--->8---
and (2) becomes
---8<---
while(imQ.empty() and delQ.empty()) { condition.wait(); }
--->8---
but this is not necessary.

Maybe the above is a general pattern to avoid complicated loop
conditions for spurious wakeups. If this is true, besides convenience, I
have no argument left for encapsulating spurious wakeups.

Thank you

regards

Alexander




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