Boost logo

Boost :

From: Raoul Gough (RaoulGough_at_[hidden])
Date: 2004-02-21 10:33:21


John Fuller <jfuller_at_[hidden]> writes:

>
> On Feb 13, 2004, at 2:23 AM, Vladimir Prus wrote:
>
>> John Fuller wrote:
>>
>>> both Ted Yuan's producer-consumer templates (cujjan2004)
>>> and the synchronized template
>>> at http://libcalc.sourceforge.net/synchronized.hpp
>>> are nice thread-safe container implementations...
>>
>> I could not find the first reference. Looking at the second, it
>> seems to
>> implement the same idea as Raoul Gough's presented. And of course,
>> I'll ask
>> the same question: if I have code like
>>
>> if (!proxy->empty()) {
>> int value = proxy->front();
>> //....
>> }
>>
>> what prevents other thread from extracting all elements between the two
>> calls?
> You're right about synchronized<> as far as containers are concerned.
>
> Here's Ted Yuan's ProducerConsumer code from CUJ Jan '04
> which does explicitly lock around these cases (ex: in offer and poll
> calls
> for a channel (a template wrapper for a container type)).

This code uses a lot of reserved names (starting underscore upper-case
letter) which is interesting. I can only assume the CUJ isn't too
bothered by that kind of stuff.

>
>
> #ifndef _PRO_CON_H
> #define _PRO_CON_H
>
> //
> // Copyright (c) 2002 by Ted T. Yuan.
> //
> // Permission is granted to use this code without restriction as
> // long as this copyright notice appears in all source files.
> //
[snip]
> // for producer thread...
> bool offer(_Tp item, long msecs = -1) // ignore msecs for now
> {
> Lock lk(monitor_);
> while (maxSize_ == ((_queueTp *)this)->size())
> {
> bufferNotFull_.wait(lk);
> }
>
> // push front
> push(item);
> bufferNotEmpty_.notify_one();
> return true;
> }

The only problem with this approach is that you have to acquire and
release the lock for every operation (e.g. each call to "offer" only
inserts one object). By using an external proxy object, it is possible
to grab the lock, check how much space is available and shutgun a
bunch of stuff into the queue in one go:

  {
    Queue::proxy_type const &proxy (gQueuePtr->getProxy());

    proxy.blockOnFull (timeout); // Queue::sUnlimitedTime);

    while (!proxy.full())
      {
        proxy->push_back (something_or_other);
        // ...
      }
  }

I guess it opens up the possibilities for misuse as well. BTW, the
proxy destructor does a notify_one or notify_all by checking for
changes in the size of container during its existence. This adds some
complexity, of course, and might be a problem with std::list.

-- 
Raoul Gough.
export LESS='-X'

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