Boost logo

Boost :

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


Vladimir Prus <ghost_at_[hidden]> writes:

> Raoul Gough wrote:
>
>>> - thread-safe queue (no immediate need, but is likely to arise in future)
>>
>> I implemented a thread-safe queue on top of Boost.Threads about 18
>> months ago. It has a locking proxy that provides access to the raw
>> container, as well as providing block-on-empty and block-on-full
>> operations. e.g.
>>
>> // typedef mt::safe_queue<std::deque<Element> > queue_type;
>> // queue_type *g_queue_ptr;
>>
>> {
>> queue_type::proxy_type const &proxy (g_queue_ptr->get_proxy());
>> // Locks mutex. Noncompliant because scoped_lock is noncopyable
>> // but works on gcc at least. This could be made compliant e.g.
>> // using shared_ptr<proxy_type>
>
> Hmm.. I don't see scoped_ptr anywhere in the example?

I forget to say, proxy_type "has-a" scoped_lock, which of course makes
the proxy_type objects also noncopyable.

>
>> proxy.block_while_empty (timeout);
>> // Uses timed_wait (temporarily releases lock)
>>
>> while (!proxy->empty()) // Accesses raw container
>> {
>> // ...
>> proxy->pop_front(); // Accesses raw container
>> }
>
> I guess proxy's operator-> locks the mutex. Hmm... what happens if

Actually, the proxy object holds the lock all the time it exists,
except during the calls to timed_wait within block_while_xxx.

>
> 1. The proxy->empty() returns false.
> 2. Another thread, using the fact that mutex is not locked now, extracts all
> the elements from queue.
> 3. The first thread executes proxy->pop_front()
>
> I might be missing something, but is wrapping of all operation in
> mutex (i.e. making them synchronized in Java-speak), makes the
> resulting container sufficiently safe?

You're absolutely right, of course. Locking within each operation
wouldn't be enough because you almost always need multiple operations
to achieve anything useful. That's why the timed_wait facility is so
good - it automatically releases the lock and reacquires it before
returning. Other than those wait operations, the proxy object
completely blocks access to the container, so you have to make sure it
gets destroyed at the right time. e.g. here's the implementation of
the somewhat high-level function safe_queue<>::push_front

template<typename Container>
class safe_queue {
  // ...
  void push_front (
      value_type const &x, int tmout_msec = s_unlimited_time)
  {
    proxy_type proxy (*this);

    proxy.block_on_full (tmout_msec);

    proxy->push_front (x);
  }
  // ...
};

If there's any interest, I could fix up the naming style in the code
to match boost conventions and post it somewhere. I don't know how
portable it would be, because of the non-copyable proxy type, but that
could be fixed as I mentioned earlier. I developed it on my own time,
so there wouldn't be any copyright issues.

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