###  | # Boost.Threads ## Header <boost/thread/condition.hpp>

## Contents

Introduction
Classes

Class `condition`

Class `condition` synopsis
Class `condition` constructors and destructor
Class `condition` modifier functions

Example(s)

## Introduction

Include the header <boost/thread/condition.hpp> to define the class condition.

## Classes

### Class `condition`

An object of class `condition` is a synchronization primitive used to cause a thread to wait until a particular shared-data condition (or time) is met. A `condition` object is always used in conjunction with a mutex object (an object whose type is a model of Mutex or one of its refinements). The mutex object must be locked prior to waiting on the `condition`, which is verified by passing a lock object (an object whose type is a model of Lock or one of its refinements) to the `condition` object's `wait` functions. Upon blocking on the condition object, the thread unlocks the mutex object. When the thread returns from a call to one of the condition object's wait functions the mutex object is again locked. The tricky unlock/lock sequence is performed automatically by the `condition` object's `wait` functions.

The `condition` type is often used to implement the Monitor Object and other important patterns (see [Schmidt 00] and [Hoare 74]). Monitors are one of the most important patterns for creating reliable multithreaded programs.

See Formal Definitions for definitions of thread states blocked and ready. Note that "waiting" is a synonym for blocked.

#### Class `condition` synopsis

    namespace boost
    {
        class condition : private boost::noncopyable // Exposition only.
           // Class condition meets the NonCopyable requirement.
        {
        public:
            condition();
            ~condition();

            void notify_one();
            void notify_all();
            template <typename Lock>
                void wait(Lock& lock);
            template <typename Lock, typename Predicate>
                void wait(Lock& lock, Predicate pred);
            template <typename Lock>
                bool timed_wait(Lock& lock, const xtime& xt);
            template <typename Lock, typename Predicate>
                bool timed_wait(Lock& lock, const xtime& XT, Predicate pred);
        };
    };

#### Class `condition` constructors and destructor

    condition();

Effects: Constructs a `condition` object.

    ~condition();

Effects: Destroys `*this`.

#### Class `condition` modifier functions

    void notify_one();

Effects: If there is a thread waiting on `*this`, change that thread's state to ready. Otherwise there is no effect.
Note: If more than one thread is waiting on the condition, it is unspecified which is made ready. After returning to a ready state the notified thread must still acquire the mutex again (which occurs within the call to one of the `condition` object's wait functions).

    void notify_all();

Effects: Change the state of all threads waiting on `*this` to ready. If there are no waiting threads, `notify_all()` has no effect.

    template <typename ScopedLock>
        void wait(ScopedLock& lock);

Requires: `ScopedLock` meets the ScopedLock requirements.
Effects: Releases the lock on the mutex model associated with `lock`, blocks the current thread of execution until readied by a call to `this->notify_one()` or `this->notify_all()`, and then reacquires the lock.
Throws: `lock_error` if `!lock.locked()`
Danger: This version should always be used within a loop checking that the state logically associated with the `condition` has become true. Without the loop, race conditions can ensue due to possible "spurious wake ups". The second version encapsulates this loop idiom internally and is generally the preferred method.

    Template<typename ScopedLock, typename Pr>
        void wait(ScopedLock& lock, Pr pred);

Requires: `ScopedLock` meets the ScopedLock requirements, return from `pred()` convertible to bool.
Effects: As if: `while (!pred()) wait(lock)`
Throws: `lock_error` if `!lock.locked()`

    template <typename ScopedLock>
        bool timed_wait(ScopedLock& lock, const xtime& XT);

Requires: `ScopedLock` meets the ScopedLock requirements.
Effects: Releases the lock on the mutex model associated with the `lock`, blocks the current thread of execution until readied by a call to `this->notify_one()` or `this->notify_all()`, or until `XT`, and then reacquires the lock.
Returns: `false` if `XT` is reached, otherwise `true`.
Throws: `lock_error` if `!lock.locked()`
Danger: This version should always be used within a loop checking that the state logically associated with the `condition` has become true. Without the loop, race conditions can ensue due to "spurious wake ups". The second version encapsulates this loop idiom internally and is generally the preferred method.

    Template<typename ScopedLock, typename Pr>
        bool timed_wait(ScopedLock& lock, const xtime& XT, Pr pred);

Requires: `ScopedLock` meets the ScopedLock requirements, return from `pred()` convertible to bool.
Effects: As if:

    while (!pred())
    {
        if (!timed_wait(lock, XT))
            return false;
    }
    return true;

Returns: `false` if `XT` is reached, otherwise `true`.
Throws: `lock_error` if `!lock.locked()`

## Example(s)

    #include <iostream>
    #include <vector>
    #include <boost/utility.hpp>
    #include <boost/thread/condition.hpp>
    #include <boost/thread/thread.hpp>

    class bounded_buffer : private boost::noncopyable
    {
    public:
        typedef boost::mutex::scoped_lock lock;

        bounded_buffer(int n) : begin(0), end(0), buffered(0), circular_buf(n) { }

        void send (int m) {
            lock lk(monitor);
            while (buffered == circular_buf.size())
                buffer_not_full.wait(lk);
            circular_buf[end] = m;
            end = (end+1) % circular_buf.size();
            ++buffered;
            buffer_not_empty.notify_one();
        }
        int receive() {
            lock lk(monitor);
            while (buffered == 0)
                buffer_not_empty.wait(lk);
            int i = circular_buf[begin];
            begin = (begin+1) % circular_buf.size();
            --buffered;
            buffer_not_full.notify_one();
            return i;
        }

    private:
        int begin, end, buffered;
        std::vector<int> circular_buf;
        boost::condition buffer_not_full, buffer_not_empty;
        boost::mutex monitor;
    };

    bounded_buffer buf(2);

    void sender() {
        int n = 0;
        while (n < 100) {
           buf.send(n);
           std::cout << "sent: " << n << std::endl;
           ++n;
        }
        buf.send(-1);
    }

    void receiver() {
        int n;
        do {
           n = buf.receive();
           std::cout << "received: " << n << std::endl;
        } while (n != -1); // -1 indicates end of buffer
    }

    int main(int, char*[])
    {
        boost::thread thrd1(&sender);
        boost::thread thrd2(&receiver);
        thrd1.join();
        thrd2.join();
        return 0;
    }

Typical output (dependent on scheduling policies) is:

    sent: 0
    sent: 1
    received: 0
    received: 1
    sent: 2
    sent: 3
    received: 2
    received: 3
    sent: 4
    received: 4

Revised 05 November, 2001

© Copyright William E. Kempf 2001-2002. All Rights Reserved.

Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. William E. Kempf makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.