Document number: N2320=07-0180

Howard E. Hinnant
Beman Dawes
Lawrence Crowl
Jeff Garland
Anthony Williams
2007-06-24

Multi-threading Library for Standard C++

Contents

Introduction

One of the major thrusts for C++0x is support for multi-threaded programs. The Library Working Group has long agreed to provide basic library facilities such as mutexes, condition variables, and threads based on the existing practice of the Boost Threads library. This proposal provides the Working Paper text for those components. Additional multi-threading components such as atomics and futures are proposed elsewhere.

This proposal is the culmination of a long line of proposals. See References.

The detailed discussion of motivations, rationales, design alternatives, and other preliminary material remains unchanged from those proposals and is not repeated in the current proposal.

This proposal assumes that exception propagation from threads will be handled as described in N2179 or its successors, and so is not included in this proposal.

At the meeting in Oxford, the committee asked Pete, Lawrence and Howard to combine the C interface of N1907 with N2184 and produce a new proposal. This effort has only been partially successful. A significant complicating factor is the introduction of cancellation, and the de-facto differences between thread cancellation in C today, and the thread cancellation proposed for C++ in N2184.

Pete produced N2285 which is a faithful reproduction of the committee's request. However N2285 lacks several of the advances in the mutex, lock and condition types which have emerged over the years based on feedback from the boost experience (N2094). The rationale sections contained herein for these types go into more detail concerning the differences between boost (N1907, N2285) and the current proposal (which closely follows the previous N2094).

An attempt was made to graft the style of the C interface in N1907 onto the mutex types of N2094. It is felt that a key use case for the C level interface is to enable interoperability between C and C++ translation units. An example use case from this early prototype was:

extern "C" void foo();

std::cnd_t cv;
std::condition cpp_cv(cv);
std::mtx_t mut;
std::mutex cpp_mut(mut);

int main()
{
   std::thread t(foo);
   t.request_cancellation();
   t.join();
}

// foo compiled as C

extern cnd_t cv;
extern mtx_t mut;

void foo()
{
  mtx_lock(&mut);
  while (true)
      cnd_wait(&cv, &mut);
  mtx_unlock(&mut);
}

There are at least two problems with the above sample code:

  1. The above example requires copying the C mutex types to the C++ mutex types (or vice versa). Prior to this the mutex types have always been non-copyable. The authors of this paper are not comfortable with copyable (or even movable) mutex types.
  2. condition::wait must be a C++ cancellation point. What happens when a C translation unit is blocked in cnd_wait and a request to cancel comes in? At this point in time we do not feel we can standardize propagating a C++ exception through a C stack frame.

At this point the C/C++ interoperability design outlined in N2145 for atomic types was brought to our attention. This approach neatly solves the first problem above by making the C and C++ types layout compatible. The C and C++ translation units can both refer to the same structure, but operate on it with different syntax and even different functions. This was prototyped and the above example use case (for mutex only) simplified to:

// A C++ translation unit

#include <mutex>

std::mutex m;

void foo()
{
    m.lock();
    // do work
    m.unlock();
}

And in the same program:

// A C translation unit

#include <mutex.h>

extern struct std_mutex m;

void bar()
{
    std_mutex_lock(&m);
    // do work
    std_mutex_unlock(&m);
}

Now mutex need not be moved, copied, or converted between C and C++ types.

However the second problem above (concerning cancellation) remained. To address this issue this proposal initially stated that the C level interface of condition::wait would return ECANCELED if it received a cancellation request. Now our example interoperability use case looks like:

// C++ translation unit

std::condition_mtx cv;
std::mutex mut;
int flag = 0;

extern "C" void f();

int main()
{
   std::thread t(f);
   t.request_cancellation();
   t.join();
}

// C translation unit

extern std_condition cv;
extern std_mutex mut;
extern int flag;

void f()
{
   std_mutex_lock(&mut);
   int ec;
   while (flag == 0)
   {
      ec = std_condition_wait(&cv, &mut);
      if (ec == ECANCELED)
          // now what?!
   }
   std_mutex_unlock(&mut);
}

As indicated by the comment in the C function f, once C++ cancellation is detected in a C translation unit, what is the C code supposed to do with it?

The C level interface has been removed from this proposal with the following rationale:

We would like to emphasize that despite the removal of the C interoperable interface from this document, we continue to believe that C/C++ interoperability in this area is important. We strongly encourage WG21 and WG14 cooperation in this area. We feel that by specifying the standard layout for the mutex/condition/thread types we have provided the necessary hooks for this interoperability. We would like to see the technical guarantees surrounding this foundation firmed up. And we would like to see prolific communication between WG21 and WG14 on this all important issue.

Thread

The thread class proposed herein follows closely to that as described in N2184. This paper has the following differences:

Creating a thread

A thread is launched by constructing a std::thread with a functor:

#include <thread>

void f();

void bar()
{
    std::thread t(f);  // f() executes in separate thread
}

The functor can be a function, or a more general class with an operator(). std::bind can be used to pass arguments to the functor. Any return type is ignored.

#include <thread>

struct f
{
    void operator()(const std::string& name, int id) {}
};

void bar()
{
    std::thread t(std::bind(f(), "Task A", 5));  // f("Task A", 5) executes in separate thread
}

Joining with a thread

A std::thread can be joined with:

t.join();  // wait for thread t to end

One can test if a thread is joinable. If the thread has already been joined with, or detached, then it is no longer joinable. If the thread has been moved from, it is no longer joinable, unless it has subsequently been moved back into.

std::thread t(f);
assert(t.joinable());

std::thread t2(std::move(t));
assert(!t.joinable());
assert(t2.joinable());

t = std::move(t2);
assert(t.joinable());
assert(!t2.joinable());

t.join();
assert(!t.joinable());

Note: It is impossible to join with the main thread as one must have a std::thread object to join with. And it is not possible to create a std::thread which refers to the main thread.

Uncaught exceptions

When a thread (other than the main thread) lets an exception go unhandled, the default behavior is to call std::terminate(). However there are many options for easily modifying this default behavior.

void f()
{
    throw 1;
}

int main()
{
    std::thread t(f);
    t.join();
}

// std::terminate() is called after f throws.

If the above is not the desired behavior, the client of f can easily wrap f in a functor using std::bind which catches unhandled exceptions and performs some other action. For example here is such a functor which logs unhandled exceptions, but does not otherwise indicate an error:

#include <fstream>
#include <thread>
#include <functional>

void f()
{
    throw 1;
}

struct log_uncaught_exceptions
{
    template <class F>
    void operator()(F f)
    {
        try
        {
            f();
        }
        catch (...)
        {
            std::ofstream("log.text", std::ios::app) << "uncaught exception\n";
        }
    }
};

int main()
{
    std::thread t(std::bind(log_uncaught_exceptions(), f));
    t.join();
}

The file log.text is appended with "uncaught exception".

Indeed, it is believed that the functor adaptor is sufficiently general that the following can be non-intrusively built upon this proposal:

thread is move-only

A std::thread is not copyable, but is moveable. This ensures that a thread can have only one parent or owner, but that ownership can be transferred among scopes, or even among threads.

// factory function for thread
std::thread
CreateMyThread(const std::string& name, int x)
{
    std::thread t(std::bind(f, name, x));
    // maybe wait for / communicate with the new thread here, maybe not...
    return t;
}
...
// Details on how you want your thread created are encapsulated
std::thread t = CreateMyThread("Task A", 26);
// threads can be stored in containers
int main()
{
    std::vector<std::thread> thread_group;
    thread_group.push_back(std::thread(f));
    ...
    // Number of threads created here not known until run time
    // (motivating the use of vector<thread> in this example)
    ...
    // Join with all of the threads
    for (auto i = thread_group.begin(), e = thread_group.end(); i != e; ++i)
        i->join();
}

Thread ID

Despite the fact that std::thread is not copyable, its id is copyable. Therefore clients can freely pass around this id. But the only thing this information can be used for is comparing the identity of threads. The id of a thread can be obtained from a joinable std::thread. Additionally a thread can obtain its own id without the use of a std::thread (including the main thread). Finally an id can be default constructed and is then guaranteed not to compare equal to the id of any other running thread.

std::thread::id id;               // Refers to no thread
assert(id == std::thread::id());  // All default constructed id's compare equal

id = std::this_thread::get_id();  // get id for this thread
assert(id != std::thread::id());  // id now refers to this thread

std::thread t(f);                 // launch a thread and call it t
id = t.get_id();                  // id now refers to t's id
assert(id != std::this_thread::get_id());

this_thread Namespace

Note the use of the this_thread namespace to disambiguate when you are requesting the id for the current thread, vs the id of a child thread. The get_id name for this action remains the same in the interest of reducing the conceptual footprint of the interface. This design also applies to the cancellation_requested function:

std::thread my_child_thread(f);
typedef std::thread::id ID:

ID my_id std::this_thread::get_id();  // The current thread's id
ID your_id my_child_thread.get_id();  // The child   thread's id

bool have_i_been_canceled = std::this_thread::cancellation_requested();  // Current thread's cancellation status
bool have_you_been_canceled = my_child_thread.cancellation_requested();  // Child   thread's cancellation status

The this_thread namespace also contains a few other functions that operate on, or query the current thread of execution.

Canceling threads

A joinable std::thread can be cooperatively canceled. When a thread cancels, all it does is throw an exception of type thread_canceled. Thus a canceled thread can cancel its cancel simply by catching (and not re-throwing) thread_canceled. One thread can request that another thread throw a thread_canceled exception with this syntax:

std::thread t(f);
t.request_cancellation();  // request that the thread referred to by t cancel itself

To actually respond to a request to cancel, a thread must execute a cancelation point. A thread can call cancellation_point() in order to turn any code into a cancellation point.

std::this_thread::cancellation_point();  // Will throw a thread_canceled exception if
                                         // and only if exceptions are enabled, and if
                                         // someone has called t.request_cancellation() where t refers
                                         // to this thread

Note that the main thread can not be canceled (by another thread) because cancellation by another thread can only be done through a std::thread and there is no way to create a std::thread which refers to the main thread.

The list of recommended cancellation points is:

A thread can disable cancellations for itself, even if it does execute a cancellation point such as cancellation_point. This is done with the disable_cancellation class. The construction of this class has the effect of disabling cancellations for the lifetime of the object. When the object destructs, cancellation is automatically reverted to its previous state (typically re-enabled).

{
std::this_thread::disable_cancellation _;
...
// cancellation can not happen here
std::this_thread::cancellation_point();  // guaranteed to have no effect
...
}  // cancellation enabled here
std::this_thread::cancellation_point();  // Will throw if someone has requested a request_cancellation()

Because cancellation is disabled with a class object, the cancellation is guaranteed to be correctly enabled whether the scope is left normally, or by an exception (whether or not that exception is thread_canceled. Note: within this document, a convention is used that if the object name is "_", that name is not used anywhere.

Cancellation is not disabled during stack unwinding. Destructors must be cancellation safe whether they are being executed due to a thread_canceled exception, or any other exception. Thus automatically disabling cancellation when a thread_canceled is thrown is redundant. And there is at least one corner case where it causes ill effects.

disable_cancellation scopes can be nested. That is, outer code can disable cancellation, and then call other functions, without fear that those functions will disable, and subsequently enable cancellation prematurely.

void foo()
{
    std::this_thread::disable_cancellation _;
    // cancellation disabled here
    ...
}   // cancellation enabled only if it was enabled upon entering foo

void bar()
{
    std::this_thread::disable_cancellation _;
    // cancellation disabled here
    foo();
    // cancellation still disabled here
}  // cancellation enabled only if it was enabled upon entering bar

If the main thread calls this_thread::cancellation_point() or constructs an object of type this_thread::disable_cancellation, there is no effect. The main thread can not be canceled. Having these functions silently ignore the main thread allows library code to use this functionality without worry of whether it is being executed in the main thread or not.

One can request permission from a disable_cancellation object to temporarily re-enable cancellation inside the scope. This requires a non-const disable_cancellation object:

void foo()
{
    std::this_thread::disable_cancellation no_cancel;
    A a;      // this may not be cancelled
    std::this_thread::restore_cancellation _(no_cancel);
    a.foo();  // this may be cancelled
}  // disable cancellation, a.~A(), restore cancellation

The restore_cancellation constructor simply reverts to the cancellation state that was in effect with the referenced disable_cancellation object was constructed. Thus restore_cancellation doesn't actually enable cancellations unless the referenced disable_cancellation was not constructed within the scope of another disable_cancellation. Thus when a function says:

void bar()
{
    std::this_thread::disable_cancellation _;
    foo();
}

then it is not possible for code within the function foo to enable cancellation (even if it tries to as with this example). To enable cancellation in a called function, bar would have to communicate the name of its disable_cancellation object to that function.

This design has been set up to provide flexibility for disabling and enabling cancellation, yet prevent accidental enabling when calling unknown code, and to prevent accidentally not re-enabling when exceptions propagate through a stack frame.

Destructing a thread

Every thread must either be joined or detached within its lifetime. To support cooperative cancellation, the thread destructor must be prepared to deal with threads which have neither been joined or detached. Consider for example a cancelable thread that owns two child threads:

std::thread t1(f1);
std::thread t2(f2);
t1.join();
t2.join();  // what does t2.~thread() do if t1 throws thread_cancelled?

Upon cancellation of this thread (t1.join() is a cancellation point), a thread_canceled exception propagates through the stack frame, destructing t2 before it has had a chance to join. If t2 simply detaches, then t2 may run for an arbitrarily long time, and consume arbitrarily large resources. This may result in the cancellation request of the parent thread effectively not being honored. Thus when a thread is destructed, if it is joinable then it is first canceled, and then detached. This allows the parent (canceling) thread to continue to cancel without blocking, and yet notify all of its child threads of the cancellation request.

If semantics other than request_cancellation(); detach(); are desired for a thread destructor, this is easy to arrange for with a thread manager object. Such an object would be a scope guard for a thread which points to the desired functionality upon destruction. Smart pointers with policy destructors are easily and efficiently employed as scope guards.

Threading cooperatively

Namespace this_thread has two functions for yielding processor control to another thread:

void yield();
template <class ElapsedTime> void sleep(const ElapsedTime& rel_time);

A thread can call these functions to control its own yielding of the processor. There is no way to request another thread to yield or sleep.

Note: See N2328 for more details on handling time durations.

Environment

There is one static member function of thread which yields a measure of the number of threads which could possibly execute concurrently:

unsigned n = std::thread::hardware_concurrency();

This can come in handy in a variety of situations such as sizing a thread pool.

Mutex

Mutex Rationale and Examples

Below is shown the basic operation of a mutex. Normally one will want to lock and unlock mutexes using scoped_lock<mutex> or unique_lock<mutex>. However lock, try_lock and unlock member functions are available in the mutex types themselves to provide flexibility to the client.

#include <mutex>

std::mutex m;

void foo()
{
    m.lock();
    // do work
    m.unlock();
}

Mutex concepts

Boost separates mutex concepts out into:

  1. Mutex
  2. TryMutex
  3. TimedMutex

Each of these three concepts have both recursive and non-recursive counterparts for a total of 6 concepts.

  1. Mutex
  2. RecursiveMutex
  3. TryMutex
  4. RecursiveTryMutex
  5. TimedMutex
  6. RecursiveTimedMutex

Because of anticipated support in the future for more mutex concepts (such as read/write) an attempt has been made to reduce the number mutex concepts. It was noted that all mutex concepts can support the TryMutex concept without extra expense. Therefore the TryMutex concept has been eliminated and folded into the Mutex concept:

  1. Mutex
  2. RecursiveMutex
  3. TimedMutex
  4. RecursiveTimedMutex

It is shown later that the TryMutex concept is a necessary requirement for fundamental generic lock algorithms such as std::lock(L1&, L2&, L3&...) and is thus a good idea to include as a fundamental requirement for all mutex types (high benefit, zero cost).

Time Issues

Most of the time-related interface is based on time durations (e.g. milliseconds(100)) instead of specific points in time (eg: 2007-May-28 00:00:00.12345). The one exception to this policy is the timed_wait member of the condition variable. In this case spurious wake ups are expected, and when this happens, without timing against a specific point in time, it is difficult to know whether you've woken up for spurious reasons or because of a time out, and if for spurious reasons how much longer you need to wait for. Therefore timed_wait on condition variables alone is specified in terms of a specific point in time. Every effort has been made to anticipate the TR2 date_time support and make the standard interface compatible with that. See N2328 for details.

Examples

std::timed_mutex mut;
std::basic_condition<std::unique_lock<std::mutex>> cv;

void foo()
{
    std::unique_lock<std::mutex> lk(mut);
    // Wait for 2 seconds on a condition variable
    std::utc_time time_out = std::hiresolution_clock::universal_time() + std::seconds(2);
    while (!pred)
    {
        bool timed_out = !cv.timed_wait(lk, time_out);
        if (timed_out)
            // deal with time out
    }
}

void bar()
{
    // Wait for 1/10 of a second on a mutex
    if (mut.timed_lock(std::milliseconds(10)))
        // got the lock
}

Lock Rationale and Examples

Unlike boost locks, the locks proposed herein are not nested types of the mutex classes but class templates which are templated on the mutex type. The locks thus become far more reusable. They can be instantiated with any standard or user-defined mutex which meets the mutex requirements.

std::scoped_lock<std::mutex> lock1(m1);  // ok
...
std::scoped_lock<std::timed_mutex> lock2(m2);  // also ok
...
std::scoped_lock<my_mutex> lock3(m3);  // also ok

There are two lock class templates:

  1. template <class Mutex> scoped_lock
  2. template <class Mutex> unique_lock

The purpose of scoped_lock is to serve the common use case with as much efficiency as possible. Unlike the boost scoped_lock, this scoped_lock always owns its referenced mutex. There need be no internal flag indicating ownership. The scoped_lock destructor does not need to perform a test to see if it should unlock the mutex: it unconditionally unlocks the mutex. Thus there is no branch which might stall a processor pipeline just to unlock the mutex.

Using a scoped_lock also easily signals intent: The referenced mutex is locked and unlocked strictly within the containing scope. There is no need for the reader of the code to search for places where mutex ownership might be transferred out of the current scope.

void foo()
{
    std::scoped_lock<std::mutex> _(mut);
    // do protected work
    // ...
}   // mut unlocked

It is not possible to have a scoped_lock that does not refer to a mutex. And it is not possible for that referenced mutex to not be locked by the scoped_lock. The only way to lock the mutex is with the scoped_lock constructor, and the only way to unlock it is with the scoped_lock destructor. This is far more restrictive than the boost scoped_lock, but slightly more efficient as well. Because of the prevalence of the scoped locking pattern, it is felt by the authors that a maximally efficient lock dedicated to this use case is justified.

Because there exist use cases which require more flexibility than a strict scoped style locking pattern, unique_lock is introduced. Unlike scoped_lock, unique_lock may or may not reference a mutex, and if it does, may or may not own the locked state of that mutex. This is much more like the semantics of the boost scoped_lock. However, unlike the boost scoped_lock, unique_lock services all of the mutex concepts (timed and non-timed). A unique_lock is movable, but not copyable, so they can be put into containers and returned from factory functions.

template <class L1, class L2>
int
try_lock(L1& lock1, L2& lock2)
{
    unique_lock<L1> ul(lock1, try_to_lock);
    if (ul.owns())
    {
        if (lock2.try_lock())
        {
            ul.release();
            return -1;
        }
        return 1;
    }
    return 0;
}

In the example above, unique_lock serves to provide exception safety, unlocking lock1 if the attempt to lock lock2 throws an exception. However, because strict scoped locking isn't desired in this use case, the unique_lock is asked to release its lock ownership if both lock1 and lock2 are successfully locked.

Also note in the above example that L1 and L2 may also be unique_lock types. Because of the generality of the templated locks (as opposed to being available only as nested types of a mutex), the try_lock algorithm can easily and seamlessly create a unique_lock<unique_lock<Mutex>> type (ul in the example if L1 is a unique_lock<Mutex>).

Finally note a few syntactic differences between boost scoped_lock and unique_lock which lead to improved readability:

Looking forward, TR2 may have a new lock type that does not model exclusive ownership as scoped_lock and unique_lock do, but rather models shared ownership. A reasonable name for such a lock might be shared_lock.

We feel that the above is an appropriate naming convention for the various lock types.

Generic Locking Algorithm Rationale and Examples

Consider a user written class which contains a data member mutex which controls access to the object:

class Record
{
    mutable std::mutex mut;
    ...
public:
    ...
};

Now consider writing the assignment operator for this class:

Record&
Record::operator=(const Record& r)
{
    if (this != &r)
    {
        std::scoped_lock<std::mutex> this_lock(mut);
        std::scoped_lock<std::mutex> that_lock(r.mut);
        // Both source and destination are locked
        // Safe to assign
        // ...
    }
    return *this;
}

Unfortunately the above code is wrong and can lead to deadlock. Given two objects of type Record, r1 and r2, if one thread executes r1 = r2 while at the same time another thread executes r2 = r1, then it is possible to deadlock. For example:

Thread A                Thread B
lock r1.mut             lock r2.mut
block on r2.mut         block on r1.mut

To address this situation a generic locking algorithm is provided which locks an arbitrary number of locks at the same time while avoiding deadlock:

template <class L1, class L2, class ...L3> void lock(L1&, L2&, L3&...);

Now the assignment operator can easily be written to avoid deadlock:

Record&
Record::operator=(const Record& r)
{
    if (this != &r)
    {
        std::unique_lock<std::mutex> this_lock(mut, std::defer_lock);
        std::unique_lock<std::mutex> that_lock(r.mut, std::defer_lock);
        std::lock(this_lock, that_lock);
        // Both source and destination are locked
        // Safe to assign
        // ...
    }
    return *this;
}

unique_lock is now required instead of scoped_lock as one can not defer the locking of the mutex within a scoped_lock. Note too that the locks locked with std::lock do not need to be the same type. So if we have read/write locking in the future this might look like:

Record&
Record::operator=(const Record& r)
{
    if (this != &r)
    {
        std::unique_lock<std::tr2::rw_mutex>      this_lock(mut, std::defer_lock);
        std::tr2::shared_lock<std::tr2::rw_mutex> that_lock(r.mut, std::defer_lock);
        std::lock(this_lock, that_lock);
        // Both source and destination are locked
        // Safe to assign
        // ...
    }
    return *this;
}

In the above example this is write-locked and r is read-locked, all done in a deadlock-safe manner.

Condition Variables

Condition variables are a inter-thread notification mechanism which work closely with mutexes and locks. The typical use case is for a thread to lock a mutex (or lock) associated with some data which is used to compute a predicate (e.g. does the queue have items). When the predicate is false, the thread will wait on the condition variable using the still locked mutex as an argument to the wait function. The locked mutex assures that no other thread can change the protected data while the current thread-of-execution is in the process of blocking (waiting) for that data to be updated. Once the waiting thread is blocked, the system unlocks the mutex so that another thread can lock the mutex, update the protected data, unlock the mutex, and signal the condition variable to wake one or more threads to process the protected data.

Condition Rationale and Examples

std::mutex mut;
std::condition_ulm cv;
std::queue<int> data;

void thread1()  // consumer
{
    while (true)
    {
        int d;
        {
        std::unique_lock<std::mutex> lk(mut);  // Protect data
        while (data.empty())                   // Is there data to process?
            cv.wait(lk);                       // Sleep and release data lock
        d = data.front();                      // Remove data from non-empty
        data.pop();                            //    queue with mutex locked
        }
        process(d);                            // Process data with mutex unlocked
    }
}

void thread2()  // producer
{  
    while (true)
    {
        int d = get_more_data();               // Produce data with mutex unlocked
        std::scoped_lock<std::mutex> _(mut);  // Protect data
        data.push(d);                          // get data and push it into queue
        if (data.size() == 1)
            cv.notify_one();                   // Notify thread1 that data queue has become non-empty
    }
}

The example above demonstrates basic condition usage. The condition type: condition_ulm is a typedef for condition<unique_lock<mutex>> (the ulm is an acronym for unique_lock<mutex>). thread1 acts as a consumer, waiting until there is data in the queue to process. The queue is checked, and data is removed from the queue under the protection of a std::mutex which is locked with a std::unique_lock. While thread1 waits for the empty queue to have data pushed into it, the system unlocks the mutex.

thread2 in the above example supplies data to the queue. As it is accesses the shared data queue it protects it with the std::mutex. When thread2 detects that the queue has transitioned from empty to one element, it signals thread1 via the condition variable. If thread1 isn't blocked on the condition variable at this time, the notification is harmlessly ignored.

Both thread1 and thread2 do as much processing as possible with the std::mutex unlocked, thus increasing overall throughput.

Condition Variable Flexibility

A template class condition<Lock> is supplied, where the only requirements on Lock are that it support lock() and unlock() member functions. Lock could be any of the standard mutexes or locks, or any user defined mutex or lock (as long as they meet the lock/unlock requirements.

Assuming that TR2 brings read/write mutexes and shared locks, they will be usable with this std::condition. Being able to wait with a read/write mutex, locked either for reading or writing, goes significantly beyond Posix capabilities and Boost capabilities (though Windows Vista has this capability).

std::tr2::rw_mutex mut;
std::condition<std::tr2::shared_lock<std::tr2::rw_mutex>> cv;

void foo()
{
    std::tr2::shared_lock<std::tr2::rw_mutex> read_lock(mut);
    while (there_is_nothing_to_read())
        cv.wait(read_lock);
    ...
}

This pattern might allow a single producer, needing a write lock, to signal many consumers which need only read locks to "consume", which finally might signal a single clean up thread needing a write lock to dispose of the data.

Proposed wording


Chapter 30   Multi-threading library


This clause describes components that C++ programs may use to create and manage multi-threaded programs.

The following subclauses describe components to create and manage threads-of-execution, perform mutual exclusion and locking, and communicate between threads-of-execution.

Subclause Header(s)
Threads <thread>
Mutexs and locks <mutex>
Condition variables <condition>

Some functions described in this clause are specified to throw exceptions of type system_error ([syserr.syserr]). The error_category ([syserr.errcat.overview]) of the error_code reported by such exceptions code() member function is implementation-defined. [Note: The category is typically native_category ([syserr.errcat.overview]) since these error codes usually originate from the underlying operating system application program interface (API). -- end note]

Threads

<thread> synopsis

namespace std {

class thread_canceled;
class thread;

void swap(thread&  x, thread&  y);
void swap(thread&& x, thread&  y);
void swap(thread&  x, thread&& y);

class thread::id;
bool operator==(const thread::id& x, const thread::id& y);
bool operator!=(const thread::id& x, const thread::id& y);

template<class charT, class traits>
basic_ostream<charT, traits>&
operator<< (basic_ostream<charT, traits>&& out, const thread::id& id);

namespace this_thread
{
    class disable_cancellation;
    class restore_cancellation;

    void cancellation_point();
    bool cancellation_enabled();
    bool cancellation_requested();
    
    thread::id get_id();
    
    void yield();
    template <class ElapsedTime>
        void sleep(const ElapsedTime& rel_t);

}  // this_thread

struct once_flag
{
    constexpr once_flag();

    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
};

template<typename Callable, typename Args...>
void call_once(once_flag& flag, Callable func, Args... args);

}  // std

Class thread_canceled

An exception class thread_canceled is thrown to execute a cancellation request. [Note: thread_canceled does not derive from exception to avoid being caught by accident. --end note]

class thread_canceled
{
public:
    thread_canceled();
    virtual ~thread_canceled();
    virtual const char* what() const;
};
thread_canceled()

Effects: Constructs an object of type thread_canceled.

Throws: Nothing.

~thread_canceled()

Effects: Destructs an object of type thread_canceled.

Throws: Nothing.

const char* what()

Returns: An implementation-defined NTBS.

Throws: Nothing.

Remarks: The message may be a null-terminated multibyte string ([multibyte.strings]), suitable for conversion and display as a wstring ([string.classes], [locale.codecvt]). The return value remains valid until the exception object from which it is obtained is destroyed or a non-const member function of the exception object is called.

Class thread

An object of class thread launches a new thread-of-execution, and provides mechanisms for the current thread-of-execution to wait for completion of the launched thread, request cooperative cancellation of the launched thread, and perform other operations to manage and query the thread's state.

class thread
{
public:
    thread();
    template <class F> explicit thread(F f);
    ~thread();

    thread(const thread&) = delete;
    thread& operator=(const thread&) = delete;

    thread(thread&&);
    thread& operator=(thread&&);

    void swap(thread&&);

    void request_cancellation();
    bool cancellation_requested() const;

    bool joinable() const;
    void join();
    template <class ElapsedTime>
        bool timed_join(const ElapsedTime& rel_t);
    void detach();

    class id
    {
    public:
        id();
        friend bool operator==(const id& x, const id& y);
        friend bool operator!=(const id& x, const id& y);
        friend bool operator<(const id& x, const id& y);
        friend bool operator<=(const id& x, const id& y);
        friend bool operator>(const id& x, const id& y);
        friend bool operator>=(const id& x, const id& y);
    };

    id get_id() const;

    typedef implementation-defined native_handle_type;
    native_handle_type native_handle();

    static unsigned hardware_concurrency();
};

Class thread and class thread::id shall be standard-layout types ([?]).

thread();

Effects: Constructs an object of type thread.

Postconditions:

get_id() == thread::id()
&& joinable() == false

Remarks: get_id() returns an identity that refers to not any thread. This identity compares equal to other non-joinable threads, and compares not equal to all other joinable threads.

Throws: Nothing.

template <class F> explicit thread(F f)

Requires: If f is an lvalue, F must be CopyConstructible. If f is an rvalue, F must only be MoveConstructible.

Effects: Constructs an object of type thread and executes the functor f asynchronously as a new thread-of-execution. F is a functor which takes no argument. Any return value from the functor is ignored. If f terminates with an uncaught exception of type thread_canceled, or of type publicly derived from thread_canceled, then the effect shall be as if f returned normally. If f terminates with an uncaught exception of any other type, std::terminate() shall be called.

Postconditions:

get_id() != thread::id()
&& joinable() == true

For the newly created thread-of-execution, this_thread::cancellation_enabled() is true. [Note: cancellation is enabled upon thread creation. -- end note]

*this represents the newly started thread-of-execution.

Throws: system_error if unable to start this thread.

~thread()

Effects: If joinable() then request_cancellation() followed by detach(), otherwise no effects.

Throws: Nothing.

thread(thread&& x)

Effects: Constructs an object of type thread from x.

Postconditions: x.joinable() is false. x.get_id() == thread().get_id(). joinable() returns the value of x.joinable() prior to the start of construction. get_id() returns the value of x.get_id() prior to the start ofconstruction.

Throws: Nothing.

thread& operator=(thread&& x)

Effects: If this currently refers to a joinable thread, calls request_cancellation() and detach(). Then assigns the state of x to *this and sets x to a default constructed state.

Postconditions: x.joinable() is false. x.get_id() == thread().get_id(). joinable() returns the value of x.joinable() prior to the assignment. get_id() returns the value of x.get_id() prior to the assignment.

Throws: Nothing.

void swap(thread&& x)

Effects: Swaps the state of *this and x.

Throws: Nothing.

void request_cancellation()

Preconditions: joinable() is true.

Postcondition: cancellation_requested() is true.

Throws: Nothing.

bool cancellation_requested() const

Preconditions: joinable() is true.

Returns: For the thread-of-execution represented by *this, this_thread::cancellation_requested().

Throws: Nothing.

bool joinable() const

Returns: get_id() != id().

Throws: Nothing.

void join()

Preconditions: joinable() is true.

Effects: The current thread-of-execution blocks until the thread-of-execution represented by *this completes.

Postconditions: After a normal return of join(), joinable() is false. An exceptional return will indicate that the thread-of-execution represented by *this has received a request to cancel. In such an event, the thread-of-execution represented by *this remains unaffected.

Throws: If, for the thread-of-execution represented by *this, this_thread::cancellation_requested() becomes true during the call to join(), throws thread_canceled.

Remarks: This function is a cancellation point for the current thread-of-execution. [Note: The main thread can not be canceled even at a cancellation point. --end note]

template <class ElapsedTime>
    bool timed_join(const ElapsedTime& rel_t)

Requires: ElapsedTime shall be explicitly convertible to nanoseconds.

Preconditions: joinable() is true.

Effects: The current thread-of-execution blocks until the the thread-of-execution represented by *this completes, or until the indicated time duration expires.

Postconditions: If timed_join returns true, joinable() shall be false. An exceptional return will indicate that the current thread-of-execution has received a request to cancel. In such an event, the the thread-of-execution represented by *this remains unaffected.

Returns: true if the thread-of-execution represented by *this joined, otherwise false.

Throws: If, for the thread-of-execution represented by *this, this_thread::cancellation_requested() becomes true during the call to join(), throws thread_canceled.

Remarks: This function is a cancellation point for the current thread-of-execution. [Note: The main thread can not be canceled even at a cancellation point. --end note]

void detach()

Preconditions: joinable() is true.

Effects: The thread-of-execution represented by *this continues execution. When the thread-of-execution represented by *this ends execution it shall release any owned resources.

Postconditions: joinable() is false. *this does not represent a thread-of-execution.

Throws: Nothing.

thread::id()

Effects: Constructs an object of type thread::id which compares equal to other default constructed thread::id objects.

Throws: Nothing.

bool operator==(const id& x, const id& y)

Returns: If x and y both represent not any thread, then returns true. Otherwise if x and y represent the same thread-of-execution, then returns true. Otherwise returns false.

Throws: Nothing.

bool operator!=(const id& x, const id& y)

Returns: !(x == y)

Throws: Nothing.

bool operator<(const thread_id& x, const thread_id& y)

Returns: Provides an ordering for all objects of type thread_id, such that objects of type thread_id can be used as a key in Associate Containers. For two objects of type thread_id, x and y, if x == y returns true, both x < y and y < x shall return false. Otherwise, precisely one of x < y and y < x shall return true.

Throws: Nothing.

bool operator<=(const thread_id& x, const thread_id& y)

Returns: !(y < x)

Throws: Nothing.

bool operator>(const thread_id& x, const thread_id& y)

Returns: y < x

Throws: Nothing.

bool operator>=(const thread_id& x, const thread_id& y)

Returns: !(x < y)

Throws: Nothing.

id get_id() const

Returns: A thread::id which refers to the thread-of-execution represented by *this. If this thread is not joinable() returns a default constructed id.

Throws: Nothing.

native_handle_type native_handle()

Returns: An implementation defined type representing the underlying OS thread handle.

Throws: Nothing.

unsigned hardware_concurrency()

Returns: The number of threads that can reasonably be expected to execute concurrently. [Note: This value should only be considered to be a hint. --end note] If this value is not computable or well defined a return value of 1 is recommended, but not required.

Throws: Nothing.

void swap(thread&  x, thread&  y);
void swap(thread&& x, thread&  y);
void swap(thread&  x, thread&& y);

Effects: x.swap(y).

template<class charT, class traits>
basic_ostream<charT, traits>&
operator<< (basic_ostream<charT, traits>&& out, const thread::id& id);

Effects: Inserts an unspecified text representation of the thread::id into the stream out.

Returns: out.

namespace this_thread
{

    class disable_cancellation
    {
    public:
        disable_cancellation();
        ~disable_cancellation();

        disable_cancellation(const disable_cancellation&) = delete;
        disable_cancellation& operator=(const disable_cancellation&) = delete;
    };
}  // this_thread
disable_cancellation()

Effects: Constructs an object of type disable_cancellation. The construction has the effect of disabling requests to cancel the current thread-of-execution from other threads during the lifetime of this object (except as modified by restore_cancellation). When a cancellation point is executed within the lifetime of this object, a request to cancel has no affect (except as modified by restore_cancellation). The constructor also notes the current cancellation state so that it can be restored at the time this object is destructed.

Throws: Nothing.

Postconditions: this_thread::cancellation_enabled() returns false.

Remarks: This function has no effect if executed from the main thread.

~disable_cancellation()

Effects: Restores the enable-cancellation state to the same as it was when this disable_cancellation was constructed.

Throws: Nothing.

Remarks: This function has no effect if executed from the main thread.

namespace this_thread
{
    class restore_cancellation
    {
    public:
        explicit restore_cancellation(disable_cancellation&);
        ~restore_cancellation();

        restore_cancellation(const restore_cancellation&) = delete;
        restore_cancellation& operator=(const restore_cancellation&) = delete;
    };
}  // this_thread
restore_cancellation(disable_cancellation& d)

Effects: Constructs an object of type restore_cancellation. The enable-cancellation state is set to the same state that would be observed immediately after d.~disable_cancellation().

Note: The enable-cancellation may not necessarily be enabled if this construction happens within nested scopes of disable_cancellation objects.

Throws: Nothing.

Remarks: This function has no effect if executed from the main thread.

~restore_cancellation()

Effects: Disables cancellation.

Postconditions: this_thread::cancellation_enabled() returns false.

Throws: Nothing.

Remarks: This function has no effect if executed from the main thread.

namespace this_thread
{
    void cancellation_point();
    bool cancellation_enabled();
    bool cancellation_requested();
    
    thread::id get_id();
    
    void yield();
    template <class ElapsedTime>
        void sleep(const ElapsedTime& rel_t);

}  // this_thread
void cancellation_point()

Effects: If cancellation_enabled() && cancellation_requested() then throws an exception of type thread_canceled, else there is no effect.

Postconditions: If a thread_canceled is thrown, then cancellation_requested() shall be false.

Throws: thread_canceled.

bool cancellation_enabled()

Returns: If this is the main thread, returns false. Otherwise returns true unless a disable_cancellation object has been constructed (and not destructed) and which has not been reverted with a restore_cancellation object.

Throws: Nothing.

bool cancellation_requested()

Returns: true if request_cancellation() has been called on this thread and the thread has not yet executed a cancellation point with cancellation enabled.

Throws: Nothing.

thread::id this_thread::get_id()

Returns: Returns the id of the current thread. The return shall not be equal to a default constructed thread::id.

Throws: Nothing.

void yield()

Effects: Offers the operating system the chance to schedule another thread.

Throws: Nothing.

template <class ElapsedTime>
    void sleep(const ElapsedTime& rel_t)

Requires: ElapsedTime shall be explicitly convertible to nanoseconds.

Effects: The current thread-of-execution blocks for at least the amount of time specified, unless it receives a request to cancel.

Throws: Nothing.

Remarks: This function is a cancellation point.

struct once_flag

Objects of class once_flag are opaque data structures that allow call_once to initialize data without causing a data race or deadlock.

constexpr once_flag();

Effects: Constructs a object of type once_flag.

Postcondition: Internal state is set to indicate to an invocation of call_once with this once_flag as its initial argument that no function has been called.

non-member function call_once

template<typename Callable, typename Args...>
void call_once(once_flag& flag, Callable func, Args... args);

Requires: If the Callable argument func is an lvalue, F is CopyConstructible. Otherwise, func is an rvalue, and F is MoveConstructible. Copying or moving (as appropriate) shall have no side effects, and the effect of calling the copy shall be equivalent to calling the original.

Effects: The argument func (or a copy thereof) is called exactly once for the once_flag object specified by flag, as-if by invoking func(args), even if call_once is called multiple times for the same once_flag object. If multiple calls to call_once with the same once_flag object occur in separate threads-of-execution, only one thread shall call func, and none of the threads shall proceed until the call to func has completed. If the invocation of func results in an exception being thrown, the exception is propagated to the caller and the effects are as-if this invocation of call_once did not occur.

Throws: system_error or any exception propagated from func.

Thread safety: Access to the same once_flag object by calls to call_once from different threads-of-execution shall not result in a data race or deadlock.

[Examples:

std::once_flag flag;

void init();

void f()
{
    std::call_once(flag,init);
}

struct initializer
{
    void operator()();
};

void g()
{
    static std::once_flag flag2;
    std::call_once(flag2,initializer());
}

-- end example]

Mutexs and locks

<mutex> synopsis

namespace std {

struct static_mutex;
struct mutex;
struct recursive_mutex;
struct timed_mutex;
struct recursive_timed_mutex;

struct defer_lock_type;
struct try_lock_type;
struct accept_ownership_type;

extern defer_lock_type       defer_lock;
extern try_lock_type         try_to_lock;
extern accept_ownership_type accept_ownership;

class lock_error;

template <class Mutex> class scoped_lock;
template <class Mutex> class unique_lock;

template <class Mutex> void swap(unique_lock<Mutex>&  x, unique_lock<Mutex>&  y);
template <class Mutex> void swap(unique_lock<Mutex>&& x, unique_lock<Mutex>&  y);
template <class Mutex> void swap(unique_lock<Mutex>&  x, unique_lock<Mutex>&& y);

template <class L1, class L2, class ...L3> int try_lock(L1&, L2&, L3&...);
template <class L1, class L2, class ...L3> void lock(L1&, L2&, L3&...);

}  // std

Mutex concepts

Objects of the mutex types enforce mutual exclusion between threads-of-execution by limiting ownership of a mutex object to a single thread-of-execution. A thread-of-execution gets ownership of a mutex object by calling lock() and relinquishes ownership by calling unlock(). Ownership can not be transferred from one thread-of-execution to another. The same thread-of-execution that calls lock() for a mutex object must call unlock() for the object. Mutexes can be either recursive or non-recursive. The syntax is the same for both recursive and non-recursive mutexes, but the semantics for the member functions differs as described below.

Each mutex type shall be default constructible and destructible. If the default construction of the Mutex type fails, an exception of type system_error shall be thrown. The destructor of the Mutex type shall not throw an exception. Mutex types are neither copyable nor movable. Each mutex type shall have the following member functions:

void lock();

Precondition: For non-recursive mutexes the current thread-of-execution shall not own the mutex.

Effects: The current thread-of-execution will block until the mutex is not owned by another thread-of-execution. Upon successful completion, the current thread-of-execution owns the mutex.

Throws: system_error.

Thread safety: Calls from different threads-of-execution to lock, try_lock, and unlock functions on an object of a mutex type shall not result in data races or deadlocks.

bool try_lock();

Precondition: For non-recursive mutexes the current thread-of-execution shall not own the mutex.

Effects: If ownership can be obtained without blocking, then ownership is obtained, else there is no effect and try_lock() immediately returns.

Returns: true if ownership was obtained, otherwise false.

Thread safety: Calls from different threads-of-execution to lock, try_lock, and unlock functions on an object of a mutex type shall not result in data races or deadlocks.

Throws: Nothing.

void unlock();

Precondition: The current thread-of-execution shall own the mutex.

Effects: For a non-recursive mutex ownership is released. For a recursive mutex unlock() must be called the same number of times which the mutex was locked (via either lock() or try_lock() or by any other locking function) before ownership is released.

Thread safety: Calls from different threads-of-execution to lock, and try_lock functions on an object of a mutex type shall not result in data races or deadlocks.

Throws: Nothing.

If and only if the mutex type is internally represented by a single data structure which can be passed to operating system specific interfaces, then there shall be a nested implementation-defined typedef native_handle_type that is an alias to this native type if it is copyable, otherwise if the native type is not copyable, is a pointer to this native type. The implementation shall document whether or not the native_handle_type typedef is present.

If the nested typedef native_handle_type exists, then there also shall be a member function native_handle() which returns a handle to this internal data structure. [Example:

class mutex
{
    pthread_mutex_t m;
public:
    typedef pthread_mutex_t* native_handle_type;
    native_handle_type native_handle() {return &m;}
    ...
};

--end example]

If there is no single operating system specific data structure which implements the mutex type, then neither the nested type native_handle_type nor the member function native_handle() shall not be present. [Example: if a recursive_mutex is implemented with both a pthread_mutex_t and a separate lock count, then there will be no native_handle_type. --end example]

Implementations may supply additional implementation defined constructors which allow further customization as afforded by the implementation or its environment.

Class static_mutex

The class static_mutex is based on a new language feature constexpr which is not yet in the working draft, nor do we have field experience with it. Should this language feature fail to deliver the static initialization behavior desired, we recommend removing static_mutex from the working paper.

namespace std {

struct static_mutex
{
public:
    constexpr static_mutex();
    ~static_mutex();

    static_mutex(const static_mutex&) = delete;
    static_mutex& operator=(const static_mutex&) = delete;

    void lock();
    bool try_lock();
    void unlock();

    typedef unspecified native_handle_type;  // conditionally present.  example: pthread_mutex_t*
    native_handle_type native_handle();      // conditionally present
};

}  // std

The class static_mutex is a non-recursive mutex. It shall be a standard-layout type ([?]), and does not require dynamic initialization. The default constructor, if dynamically initialized, shall not throw an exception.

Class mutex

namespace std {

struct mutex
{
public:
    mutex();
    ~mutex();

    mutex(const mutex&) = delete;
    mutex& operator=(const mutex&) = delete;

    void lock();
    bool try_lock();
    void unlock();

    typedef unspecified native_handle_type;  // conditionally present.  example: pthread_mutex_t*
    native_handle_type native_handle();      // conditionally present
};

}  // std

The class mutex is a non-recursive mutex which satisfies all of the Mutex requirements. It shall be a standard-layout type ([?]).

Class recursive_mutex

namespace std {

struct recursive_mutex
{
public:
    recursive_mutex();
    ~recursive_mutex();

    recursive_mutex(const recursive_mutex&) = delete;
    recursive_mutex& operator=(const recursive_mutex&) = delete;

    void lock();
    bool try_lock();
    void unlock();

    typedef unspecified native_handle_type;  // conditionally present.  example: pthread_mutex_t*
    native_handle_type native_handle();      // conditionally present
};

}  // std

The class recursive_mutex shall be a recursive mutex which satisfies all of the Mutex requirements. It shall be a standard-layout type ([?]).

Timed Mutexes

Types that meet the requirements of the Timed Mutex concept also meet the requirements of the Mutex concept and add a single member function:

template <class ElapsedTime>
    bool timed_lock(const ElapsedTime& rel_time);

Precondition: For non-recursive mutexes the current thread-of-execution shall not own the mutex. The type ElapsedTime shall be explicitly convertible to nanoseconds.

Effects: The function attempts to obtain ownership of the mutex within the specified time. If the indicated time is less than or equal to 0, the function still attempts to obtain ownership without blocking (as if by calling try_lock()). If the function returns within the specified time duration, it shall have obtained ownership.

Returns: true if ownership was obtained, otherwise false.

Thread safety: Calls to this member function from different threads-of-execution shall not result in data races or deadlocks.

Throws: Nothing.

Class timed_mutex

namespace std {

struct timed_mutex
{
public:
    timed_mutex();
    ~timed_mutex();

    timed_mutex(const timed_mutex&) = delete;
    timed_mutex& operator=(const timed_mutex&) = delete;

    void lock();
    bool try_lock();
    template <class ElapsedTime>
        bool timed_lock(const ElapsedTime& rel_time);
    void unlock();

    typedef unspecified native_handle_type;  // conditionally present.  example: pthread_mutex_t*
    native_handle_type native_handle();      // conditionally present
};

}  // std

The class timed_mutex is a non-recursive mutex that satisfies all of the Timed Mutex requirements. It shall be a standard-layout type ([?]).

Class recursive_timed_mutex

namespace std {

struct recursive_timed_mutex
{
public:
    recursive_timed_mutex();
    ~recursive_timed_mutex();

    recursive_timed_mutex(const recursive_timed_mutex&) = delete;
    recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;

    void lock();
    bool try_lock();
    template <class ElapsedTime>
        bool timed_lock(const ElapsedTime& rel_time);
    void unlock();

    typedef unspecified native_handle_type;  // conditionally present.  example: pthread_mutex_t*
    native_handle_type native_handle();      // conditionally present
};

}  // std

The class recursive_timed_mutex shall be a recursive mutex that satisfies all of the Timed Mutex requirements. It shall be a standard-layout type ([?]).

Locks

Locks are objects that hold a reference to a mutex and unlock the mutex during the lock's destruction (such as when leaving block scope). The locks do not manage the lifetime of the mutex they reference, but only the ownership status of that mutex. [Note: Locks are intended to ease the burden of unlocking the mutex under both normal and exceptional circumstances. --end note]

Some locks may take tag types which describe what should be done with the mutex in the lock's constructor.

struct defer_lock_type       {};
struct try_lock_type         {};
struct accept_ownership_type {};

extern defer_lock_type       defer_lock;
extern try_lock_type         try_to_lock;
extern accept_ownership_type accept_ownership;

An exception class lock_error derives from exception and is used to indicate improper usage of locks such as locking a mutex that the lock already owns, or unlocking a mutex that the lock does not own.

class lock_error
    : public std::exception
{
public:
    virtual const char* what() const throw();
};

Class scoped_lock

namespace std {

template <class Mutex>
class scoped_lock
{
public:
    typedef Mutex mutex_type;

    explicit scoped_lock(mutex_type& m);
    scoped_lock(mutex_type& m, accept_ownership_type);
    ~scoped_lock();

    scoped_lock(scoped_lock const&) = delete;
    scoped_lock& operator=(scoped_lock const&) = delete;

    constexpr bool owns() const;
};

}  // std

scoped_lock is used to control the ownership of a mutex within a single scope. An invariant of the scoped_lock object is that it maintains the ownership of the mutex throughout the scoped_lock's lifetime. Mutex ownership can not be deferred or transferred away from the scoped_lock.

explicit scoped_lock(mutex_type& m);

Precondition: If mutex_type is not a recursive mutex, the current thread-of-execution does not own the mutex. The lifetime of m is greater than the lifetime of the scoped_lock object.

Effects: Stores a reference to m and calls m.lock().

scoped_lock(mutex_type& m, accept_ownership_type);

Precondition: The current thread-of-execution has ownership of the mutex m. The lifetime of m is greater than the lifetime of the scoped_lock object.

Effects: Stores a reference to m and performs no other operation on it.

~scoped_lock();

Effects: m.unlock().

Throws: Nothing.

constexpr bool owns() const;

Returns: true.

Throws: Nothing.

Class unique_lock

namespace std {

template <class Mutex>
class unique_lock
{
public:
    typedef Mutex mutex_type;

    unique_lock();
    explicit unique_lock(mutex_type& m);
    unique_lock(mutex_type& m, defer_lock_type);
    unique_lock(mutex_type& m, try_lock_type);
    unique_lock(mutex_type& m, accept_ownership_type);
    ~unique_lock();

    unique_lock(unique_lock const&) = delete;
    unique_lock& operator=(unique_lock const&) = delete;

    unique_lock(unique_lock&& u);
    unique_lock& operator=(unique_lock&& u);

    void lock();
    bool try_lock();
    template <class ElapsedTime>
        bool timed_lock(const ElapsedTime& rel_t);
    void unlock();

    bool owns() const;
    operator unspecified-bool-type () const;
    mutex_type* mutex() const;

    void swap(unique_lock&& u);
    mutex_type* release();
};

template <class Mutex> void swap(unique_lock<Mutex>&  x, unique_lock<Mutex>&  y);
template <class Mutex> void swap(unique_lock<Mutex>&& x, unique_lock<Mutex>&  y);
template <class Mutex> void swap(unique_lock<Mutex>&  x, unique_lock<Mutex>&& y);

}  // std

unique_lock is used to control the ownership of a mutex within one or more scopes. Mutex ownership can be deferred or transferred away from the unique_lock. An object of type unique_lock is not copyable but is movable.

unique_lock();

Effects: Constructs an object of type unique_lock.

Postcondition:

mutex() == 0
owns() == false
explicit unique_lock(mutex_type& m);

Precondition: If mutex_type is not a recursive mutex, the current thread-of-execution does not own the mutex. The lifetime of m is greater than the lifetime of the unique_lock object.

Effects: Stores a reference to m and calls m.lock().

Postcondition:

mutex() == &m
owns() == true
unique_lock(mutex_type& m, defer_lock_type);

Precondition: If mutex_type is not a recursive mutex, the current thread-of-execution does not own the mutex. The lifetime of m is greater than the lifetime of the unique_lock object.

Effects: Stores a reference to m and performs no other operation on it.

Postcondition:

mutex() == &m
owns() == false
unique_lock(mutex_type& m, try_lock_type);

Precondition: If mutex_type is not a recursive mutex, then the current thread-of-execution does not own the mutex. The lifetime of m is greater than the lifetime of the unique_lock object.

Effects: Stores a reference to m and calls m.try_lock().

Postcondition:

mutex() == &m
owns() == The result of the call to m.try_lock()
unique_lock(mutex_type& m, accept_ownership_type);

Precondition: The current thread-of-execution has ownership of the mutex m. The lifetime of m is greater than the lifetime of the unique_lock object.

Effects: Stores a reference to m and performs no other operation on it.

Postcondition:

mutex() == &m
owns() == true
~unique_lock();

Effects: If owns() calls unlock() on the referenced mutex. Otherwise there are no effects.

Throws: Nothing.

unique_lock(unique_lock&& u);

Effects: Transfers mutex ownership (if any) from u to this.

Postcondition:

mutex() == The value of u.mutex() prior to the construction.
owns() == The value of u.owns() prior to the construction.
u.mutex() == 0
u.owns() == false

Throws: Nothing.

unique_lock& operator=(unique_lock&& u);

Effects: If owns() calls unlock(), and then transfers mutex ownership (if any) from u to this.

Postcondition:

mutex() == The value of u.mutex() prior to the construction.
owns() == The value of u.owns() prior to the construction.
u.mutex() == 0
u.owns() == false

Throws: Nothing.

Note: With a recursive mutex it is possible that both this and u own the same mutex before the assignment. In this case, this will own the mutex after the assignment (and u will not), but the mutex's lock count will be decremented by one.

void lock();

Effects: Calls lock() on the referenced mutex.

Postcondition: owns() == true.

Throws: lock_error, if on entry owns() is true.

bool try_lock();

Effects: Calls try_lock() on the referenced mutex.

Returns: The result of the call to try_lock() on the referenced mutex.

Postcondition: owns() == The result of the call to try_lock() on the referenced mutex.

Throws: lock_error, if on entry owns() is true.

template <class ElapsedTime>
   bool timed_lock(const ElapsedTime& rel_t);

Effects: Calls timed_lock(rel_t) on the referenced mutex.

Returns: The result of the call to timed_lock(rel_t) on the referenced mutex.

Postcondition: owns() == The result of the call to timed_lock(rel_t) on the referenced mutex.

Throws: lock_error, if on entry owns() is true.

void unlock();

Effects: Calls void unlock() on the referenced mutex.

Postcondition: owns() == false.

Throws: lock_error, if on entry owns() is false.

bool owns() const;

Returns: true if this owns a lock on a referenced mutex, else false.

Throws: Nothing.

operator unspecified-bool-type () const;

Returns: Non-null if owns() would return true, else returns null.

Throws: Nothing.

mutex_type* mutex() const;

Returns: A pointer to the referenced mutex, or null if there is no referenced mutex.

Throws: Nothing.

void swap(unique_lock&& u);

Effects: Swaps state with u.

Throws: Nothing.

mutex_type* release();

Returns: A pointer to the referenced mutex, or null if there is no referenced mutex.

Postcondition:

mutex() == 0
owns() == false

Throws: Nothing.

template <class Mutex> void swap(unique_lock<Mutex>&  x, unique_lock<Mutex>&  y);
template <class Mutex> void swap(unique_lock<Mutex>&& x, unique_lock<Mutex>&  y);
template <class Mutex> void swap(unique_lock<Mutex>&  x, unique_lock<Mutex>&& y);

Effects: x.swap(y).

Throws: Nothing.

Generic Locking Algorithms

template <class L1, class L2, class ...L3> int try_lock(L1&, L2&, L3&...);

Requires: Each template parameter type must supply the following member functions with semantics corresponding to the Mutex concept, except that try_lock is allowed to throw an exception. [Note: The unique_lock class template meets these requirements when suitable instantiated. --end note]

bool try_lock();
void unlock();

Effects: The functions attempts to lock all arguments without blocking by calling try_lock() on each of them. If any argument can not be locked, then all arguments which have already been locked will be unlocked. On return, either all arguments will be locked, or none of them will be locked. If an exception is thrown by a call to try_lock(), there are no effects.

Returns: If all arguments were successfully locked, returns -1. Otherwise returns a 0-based index value indicating which argument failed to lock.

template <class L1, class L2, class ...L3> void lock(L1&, L2&, L3&...);

Requires: Each template parameter type must supply the following member functions with semantics corresponding to the Mutex concept, except that try_lock is allowed to throw an exception [Note: The unique_lock class template meets these requirements when suitable instantiated. --end note]

void lock();
bool try_lock();
void unlock();

Effects: All arguments are locked with an algorithm that avoids deadlock. If an exception is thrown by a call to lock() or try_lock(), there are no effects.

Condition variables

<condition> synopsis
namespace std {

template <class Lock> class condition;

typedef condition<mutex>              condition_mtx;
typedef condition<unique_lock<mutex>> condition_ulm;

}  // std

Class template condition

An object of class template condition is a synchronization primitive used to cause a thread-of-execution to wait until notified by some other thread-of-execution that some condition is met, or a UTC[(?)] time is reached.

The Lock type must support member functions lock and unlock with the semantics of the mutex concept. All of the standard mutex types meet this requirement. Additionally Lock may provide an owns() signature returning bool as outlined for the unique_lock class template. If present, the condition class template will use this member for error checking.

namespace std {

template <class Lock>
class condition
{
public:
    typedef Lock lock_type;

    condition();
    ~condition();

    condition(const condition&) = delete;
    condition& operator=(const condition&) = delete;

    void notify_one();
    void notify_all();
    void wait(lock_type& lock);
    template <class Predicate>
        void wait(lock_type& lock, Predicate pred);
    bool timed_wait(lock_type& lock, const utc_time& abs_time);
    template <class Predicate>
        bool timed_wait(lock_type& lock, const utc_time& abs_time, Predicate pred);
};

}  // std
condition();

Effects: Constructs an object of class condition.

~condition();

Effects: Destroys the object.

Throws: Nothing.

void notify_one();

Effects: If any threads-of-execution are blocked waiting for *this, unblocks at least one those threads.

Thread safety: Calls to the wait, timed_wait, notify_one or notify_all member functions of the same condition object from different threads-of-execution shall not result in data races or deadlocks.

void notify_all();

Effects: Unblock all threads that are blocked waiting for *this.

Thread safety: Calls to the wait, timed_wait, notify_one or notify_all member functions of the same condition object from different threads-of-execution shall not result in data races or deadlocks.

void wait(lock_type& lock);

Precondition: lock is locked by the current thread-of-execution. If lock_type supports recursive locking, the lock count is one. No other thread-of-execution is waiting on this condition object unless lock is, or refers to, the same underlying mutex object.

Effects: Atomically blocks and releases the lock on lock. If the thread-of-execution is canceled while blocked, lock will be locked as the thread_canceled exception propagates out. This thread-of-execution shall unblock when another thread issues a notification to this blocked thread. The current thread-of-execution may unblock and return even in the absence of a notification.

Postcondition: lock is locked by the current thread-of-execution.

Throws: thread_canceled, system_error. If lock_type has an owns() member function and lock.owns() returns false upon entry, a lock_error is thrown.

Thread safety: Calls to the wait, timed_wait, notify_one or notify_all member functions of the same condition object from different threads-of-execution shall not result in data races or deadlocks.

Remarks: This function is a cancellation point for the calling thread. [Note: The main thread can not be canceled even at a cancellation point. --end note]

template <class Predicate>
    void wait(lock_type& lock, Predicate pred);

Effects: While pred() returns false calls wait(lock).

Note: There is no blocking if pred() is initially true.

bool timed_wait(lock_type& lock, const utc_time& abs_time);

Precondition: The lock is locked by the current thread-of-execution. If lock_type supports recursive locking, the lock count is one. No other thread-of-execution is waiting on this condition object unless lock is, or refers to, the same underlying mutex object.

Effects: Atomically blocks and releases the lock on lock. If the thread-of-execution is canceled while blocked, lock will be locked as the thread_canceled exception propagates out. If the absolute time specified by abs_time passes (that is, system time equals or exceeds abs_time) before the condition is notified, or if the absolute time specified by abs_time has already been passed at the time of the call, then false is returned. This thread-of-execution shall unblock when another thread issues a notification to this blocked thread. The current thread-of-execution may unblock and return even in the absence of a notification.

Postcondition: lock is locked by the current thread-of-execution.

Returns: true if the call to timed_wait is notified prior to the indicated timeout, otherwise returns false.

Throws: thread_canceled, system_error. If lock_type has an owns() member function and lock.owns() returns false upon entry, a lock_error is thrown.

Thread safety: Calls to the wait, timed_wait, notify_one or notify_all member functions of the same condition object from different threads-of-execution shall not result in data races or deadlocks.

Remarks: This function is a cancellation point for the calling thread. [Note: The main thread can not be canceled even at a cancellation point. --end note]

template <class _Predicate>
    bool timed_wait(lock_type& lock, const utc_time& abs_time, Predicate pred);

Effects: As if:

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

Returns: pred().

Note: There is no blocking if pred() is initially true, even if the timeout has already expired. The return value indicates whether the predicate evaluates to true, regardless of whether the timeout was triggered.

The specialization condition<mutex> shall be a standard-layout type ([?]).

References

Acknowledgments

The overall design of this threading library is based on William Kempf's Boost.Thread Library, as refined by literally hundreds of other Boost users and contributors. Dinkumware and Metrowerks (now Freescale) implementations of Boost.Thread, developed respectively by Pete Becker and Howard Hinnant, created further existing practice. Proposals by Pete Becker, Peter Dimov, Ion Gaztañaga, and Anthony Williams were also influential. Peter, Ion, and Anthony also contributed numerous critiques, suggestions, and comments on the current proposal, as did other members of an ad hoc threads working group.