This tutorial is an adaptation of chapter Concurrency of the Object-Oriented
Programming in the BETA Programming Language and of the paper of Andrei
Alexandrescu "Multithreading and the C++ Type System" to the
Boost library.

I C++11 (Boost) concurrent execution of a component is obtained by means
of the std::thread(boost::thread):

boost::threadthread1(S);

where S is a model of
Callable. The meaning
of this expression is that execution of S() will take place concurrently with the
current thread of execution executing the expression.

The following example includes a bank account of a person (Joe) and two
components, one corresponding to a bank agent depositing money in Joe's
account, and one representing Joe. Joe will only be withdrawing money
from the account:

The above example works well as long as the components bankAgent and Joe
doesn't access JoesAccount
at the same time. There is, however, no guarantee that this will not
happen. We may use a mutex to guarantee exclusive access to each bank.

Execution of the Deposit
and Withdraw operations
will no longer be able to make simultaneous access to balance.

A mutex is a simple and basic mechanism for obtaining synchronization.
In the above example it is relatively easy to be convinced that the synchronization
works correctly (in the absence of exception). In a system with several
concurrent objects and several shared objects, it may be difficult to
describe synchronization by means of mutexes. Programs that make heavy
use of mutexes may be difficult to read and write. Instead, we shall
introduce a number of generic classes for handling more complicated forms
of synchronization and communication.

With the RAII idiom we can simplify a lot this using the scoped locks.
In the code below, guard's constructor locks the passed-in object mtx_, and guard's destructor unlocks
mtx_.

The object-level locking idiom doesn't cover the entire richness of a
threading model. For example, the model above is quite deadlock-prone
when you try to coordinate multi-object transactions. Nonetheless, object-level
locking is useful in many cases, and in combination with other mechanisms
can provide a satisfactory solution to many threaded access problems
in object-oriented programs.

The BankAccount class above uses internal locking. Basically, a class
that uses internal locking guarantees that any concurrent calls to its
public member functions don't corrupt an instance of that class. This
is typically ensured by having each public member function acquire a
lock on the object upon entry. This way, for any given object of that
class, there can be only one member function call active at any moment,
so the operations are nicely serialized.

This approach is reasonably easy to implement and has an attractive simplicity.
Unfortunately, "simple" might sometimes morph into "simplistic."

Internal locking is insufficient for many real-world synchronization
tasks. Imagine that you want to implement an ATM withdrawal transaction
with the BankAccount class. The requirements are simple. The ATM transaction
consists of two withdrawals-one for the actual money and one for the
$2 commission. The two withdrawals must appear in strict sequence; that
is, no other transaction can exist between them.

Notice that now acct is being locked by Withdraw after it has already
been locked by guard. When running such code, one of two things happens.

Your mutex implementation might support the so-called recursive mutex
semantics. This means that the same thread can lock the same mutex
several times successfully. In this case, the implementation works
but has a performance overhead due to unnecessary locking. (The locking/unlocking
sequence in the two Withdraw calls is not needed but performed anyway-and
that costs time.)

Your mutex implementation might not support recursive locking, which
means that as soon as you try to acquire it the second time, it blocks-so
the ATMWithdrawal function enters the dreaded deadlock.

As boost::mutex is not recursive, we need to
use its recursive version boost::recursive_mutex.

The caller-ensured locking approach is more flexible and the most efficient,
but very dangerous. In an implementation using caller-ensured locking,
BankAccount still holds a mutex, but its member functions don't manipulate
it at all. Deposit and Withdraw are not thread-safe anymore. Instead,
the client code is responsible for locking BankAccount properly.

Obviously, the caller-ensured locking approach has a safety problem.
BankAccount's implementation code is finite, and easy to reach and maintain,
but there's an unbounded amount of client code that manipulates BankAccount
objects. In designing applications, it's important to differentiate between
requirements imposed on bounded code and unbounded code. If your class
makes undue requirements on unbounded code, that's usually a sign that
encapsulation is out the window.

To conclude, if in designing a multi-threaded class you settle on internal
locking, you expose yourself to inefficiency or deadlocks. On the other
hand, if you rely on caller-provided locking, you make your class error-prone
and difficult to use. Finally, external locking completely avoids the
issue by leaving it all to the client code.

So what to do? Ideally, the BankAccount class should do the following:

Support both locking models (internal and external).

Be efficient; that is, use no unnecessary locking.

Be safe; that is, BankAccount objects cannot be manipulated without
appropriate locking.

Let's make a worthwhile observation: Whenever you lock a BankAccount,
you do so by using a lock_guard<BankAccount> object. Turning this statement around,
wherever there's a lock_guard<BankAccount>, there's also a locked BankAccount somewhere. Thus, you can
think of-and use-a lock_guard<BankAccount> object as a permit. Owning a lock_guard<BankAccount>
gives you rights to do certain things. The lock_guard<BankAccount> object should not be copied or aliased
(it's not a transmissible permit).

As long as a permit is still alive, the BankAccount
object stays locked.

When the lock_guard<BankAccount> is destroyed, the BankAccount's mutex is released.

The net effect is that at any point in your code, having access to a
lock_guard<BankAccount>
object guarantees that a BankAccount
is locked. (You don't know exactly which BankAccount
is locked, however-an issue that we'll address soon.)

For now, let's make a couple of enhancements to the lock_guard
class template defined in Boost.Thread. We'll call the enhanced version
strict_lock. Essentially,
a strict_lock's role
is only to live on the stack as an automatic variable. strict_lock must adhere to a non-copy
and non-alias policy. strict_lock
disables copying by making the copy constructor and the assignment operator
private.

All these rules were put in place with one purpose-enforcing that owning
a strict_lock<T>
is a reasonably strong guarantee that

you locked a T object, and

that object will be unlocked at a later point.

Now that we have such a strict strict_lock,
how do we harness its power in defining a safe, flexible interface for
BankAccount? The idea is as follows:

Each of BankAccount's interface functions (in our case, Deposit and
Withdraw) comes in two overloaded variants.

One version keeps the same signature as before, and the other takes
an additional argument of type strict_lock<BankAccount>. The first version is internally
locked; the second one requires external locking. External locking
is enforced at compile time by requiring client code to create a
strict_lock<BankAccount>
object.

BankAccount avoids code bloating by having the internal locked functions
forward to the external locked functions, which do the actual job.

A little code is worth 1,000 words, a (hacked into) saying goes, so here's
the new BankAccount class:

Now, if you want the benefit of internal locking, you simply call Deposit(int) and
Withdraw(int).
If you want to use external locking, you lock the object by constructing
a strict_lock<BankAccount>
and then you call Deposit(int,strict_lock<BankAccount>&)
and Withdraw(int,strict_lock<BankAccount>&).
For example, here's the ATMWithdrawal
function implemented correctly:

This function has the best of both worlds-it's reasonably safe and efficient
at the same time.

It's worth noting that strict_lock
being a template gives extra safety compared to a straight polymorphic
approach. In such a design, BankAccount would derive from a Lockable
interface. strict_lock
would manipulate Lockable references so there's no need for templates.
This approach is sound; however, it provides fewer compile-time guarantees.
Having a strict_lock
object would only tell that some object derived from Lockable is currently
locked. In the templated approach, having a strict_lock<BankAccount> gives a stronger guarantee-it's a
BankAccount that stays
locked.

There's a weasel word in there-I mentioned that ATMWithdrawal is reasonably
safe. It's not really safe because there's no enforcement that the strict_lock<BankAccount>
object locks the appropriate BankAccount object. The type system only
ensures that some BankAccount object is locked. For example, consider
the following phony implementation of ATMWithdrawal:

This code compiles warning-free but obviously doesn't do the right thing-it
locks one account and uses another.

It's important to understand what can be enforced within the realm of
the C++ type system and what needs to be enforced at runtime. The mechanism
we've put in place so far ensures that some BankAccount object is locked
during the call to BankAccount::Withdraw(int,strict_lock<BankAccount>&).
We must enforce at runtime exactly what object is locked.

If our scheme still needs runtime checks, how is it useful? An unwary
or malicious programmer can easily lock the wrong object and manipulate
any BankAccount without actually locking it.

First, let's get the malice issue out of the way. C is a language that
requires a lot of attention and discipline from the programmer. C++ made
some progress by asking a little less of those, while still fundamentally
trusting the programmer. These languages are not concerned with malice
(as Java is, for example). After all, you can break any C/C++ design
simply by using casts "appropriately" (if appropriately is
an, er, appropriate word in this context).

The scheme is useful because the likelihood of a programmer forgetting
about any locking whatsoever is much greater than the likelihood of a
programmer who does remember about locking, but locks the wrong object.

Using strict_lock permits
compile-time checking of the most common source of errors, and runtime
checking of the less frequent problem.

Let's see how to enforce that the appropriate BankAccount object is locked.
First, we need to add a member function to the strict_lock
class template. The boolstrict_lock<T>::owns_lock(Loclable*)
function returns a reference to the locked object.

Let's also assume that, by design, AccountManager must stay locked while
accessing its BankAccount members. The question is, how can we express
this design constraint using the C++ type system? How can we state "You
have access to this BankAccount object only after locking its parent
AccountManager object"?

The solution is to use a little bridge template externally_locked
that controls access to a BankAccount.

The pattern is the same as before - to access the BankAccount object
cloaked by checkingAcct_,
you need to call get.
To call get, you need
to pass it a strict_lock<AccountManager>. The one thing you have to take care
of is to not hold pointers or references you obtained by calling get. If you do that, make sure that
you don't use them after the strict_lock has been destroyed. That is,
if you alias the cloaked objects, you're back from "the compiler
takes care of that" mode to "you must pay attention" mode.

Typically, you use externally_locked
as shown below. Suppose you want to execute an atomic transfer from your
checking account to your savings account:

We achieved two important goals. First, the declaration of checkingAcct_ and savingsAcct_
makes it clear to the code reader that that variable is protected by
a lock on an AccountManager. Second, the design makes it impossible to
manipulate the two accounts without actually locking a BankAccount.
externally_locked is
what could be called active documentation.

Now imagine that the AccountManager function needs to take a unique_lock in order to reduce the
critical regions. And at some time it needs to access to the checkingAcct_. As unique_lock
is not a strict lock the following code doesn't compile:

In order to make this code compilable we need to store either a Lockable
or a unique_lock<Lockable>
reference depending on the constructor. We also need to store which kind
of reference we have stored, and in the destructor call either to the
Lockable unlock or restore
the ownership.

This seems too complicated to me. Another possibility is to define a
nested strict lock class. The drawback is that instead of having only
one strict lock we have two and we need either to duplicate every function
taking a strict_lock
or make these function templates. The problem with template functions
is that we don't profit anymore of the C++ type system. We must add some
static metafunction that checks that the Locker parameter is a strict
lock. The problem is that we can not really check this or can we?. The
is_strict_lock metafunction
must be specialized by the strict lock developer. We need to believe
it "sur parole". The advantage is that now we can manage with
more than two strict locks without changing our code. This is really
nice.

Well let me show what this nested_strict_lock
class looks like and the impacts on the externally_locked
class and the AccountManager::AMoreComplicatedFunction
function.

First nested_strict_lock
class will store on a temporary lock the Locker,
and transfer the lock ownership on the constructor. On destruction it
will restore the ownership. Note the use of lock_traits
and that the Locker needs
to have a reference to the mutex otherwise and exception is thrown.

The externally_locked
get function is now a template function taking a Locker as parameters
instead of a strict_lock.
We can add test in debug mode that ensure that the Lockable object is
locked.

template<typenameT,typenameLockable>classexternally_locked{public:// ...template<classLocker>T&get(Locker&lock){BOOST_CONCEPT_ASSERT((StrictLockerConcept<Locker>));BOOST_STATIC_ASSERT((is_strict_lock<Locker>::value));// locker is a strict locker "sur parole" BOOST_STATIC_ASSERT((is_same<Lockable,typenamelockable_type<Locker>::type>::value));// that locks the same type #ifndefBOOST_THREAD_EXTERNALLY_LOCKED_DONT_CHECK_OWNERSHIP// define BOOST_THREAD_EXTERNALLY_LOCKED_NO_CHECK_OWNERSHIP if you don't want to check locker ownershipif(!lock)throwlock_error();// run time check throw if no locked #endif#ifdefBOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIEDif(!lock.owns_lock(&lockable_))throwlock_error();#endifreturnobj_;}};

The AccountManager::AMoreComplicatedFunction function needs
only to replace the strict_lock
by a nested_strict_lock.

A mutex object facilitates protection against data races and allows thread-safe
synchronization of data between threads. A thread obtains ownership of a
mutex object by calling one of the lock functions and relinquishes ownership
by calling the corresponding unlock function. Mutexes may be either recursive
or non-recursive, and may grant simultaneous ownership to one or many threads.
Boost.Thread supplies recursive and non-recursive
mutexes with exclusive ownership semantics, along with a shared ownership
(multiple-reader / single-writer) mutex.

The BasicLockable concept models exclusive
ownership. A type L meets
the BasicLockable requirements if
the following expressions are well-formed and have the specified semantics
(m denotes a value of type
L):

The calling thread doesn't owns the mutex if the mutex is not recursive.

Effects:

Attempt to obtain ownership for the current thread without blocking.

Synchronization:

If try_lock()
returns true, prior unlock() operations on the same object
synchronize with this operation.

Note:

Since lock()
does not synchronize with a failed subsequent try_lock(), the visibility rules are weak
enough that little would be known about the state after a failure,
even in the absence of spurious failures.

Return type:

bool.

Returns:

true if ownership
was obtained for the current thread, false
otherwise.

The user could require that the mutex passed to an algorithm is a recursive
one. Whether a lockable is recursive or not can not be checked using template
meta-programming. This is the motivation for the following trait.

The SharedLockable concept is a refinement
of the TimedLockable concept that allows
for shared ownership as well as exclusive
ownership. This is the standard multiple-reader / single-write
model: at most one thread can have exclusive ownership, and if any thread
does have exclusive ownership, no other threads can have shared or exclusive
ownership. Alternatively, many threads may have shared ownership.

A type L meets the SharedLockable requirements if
it meets the TimedLockable requirements and
the following expressions are well-formed and have the specified semantics.

Attempt to obtain shared ownership for the current thread. Blocks
until shared ownership can be obtained, or the specified duration
is elapsed. If the specified duration is already elapsed, behaves
as try_lock_shared().

Returns:

true if shared ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has shared ownership of m.

Attempt to obtain shared ownership for the current thread. Blocks
until shared ownership can be obtained, or the specified time is
reached. If the specified time has already passed, behaves as
try_lock_shared().

Returns:

true if shared ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has shared ownership of m.

Attempt to obtain shared ownership for the current thread. Blocks
until shared ownership can be obtained, or the specified time is
reached. If the specified time has already passed, behaves as
try_lock_shared().

Returns:

true if shared ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has shared ownership of m.

The UpgradeLockable concept is a refinement
of the SharedLockable concept that allows
for upgradable ownership as well as shared
ownership and exclusive ownership. This
is an extension to the multiple-reader / single-write model provided by
the SharedLockable concept: a single
thread may have upgradable ownership at the same time
as others have shared ownership. The thread with
upgradable ownership may at any time attempt to upgrade
that ownership to exclusive ownership. If no other
threads have shared ownership, the upgrade is completed immediately, and
the thread now has exclusive ownership, which must
be relinquished by a call to unlock(),
just as if it had been acquired by a call to lock().

If a thread with upgradable ownership tries to upgrade
whilst other threads have shared ownership, the attempt
will fail and the thread will block until exclusive ownership
can be acquired.

Ownership can also be downgraded as well as upgraded:
exclusive ownership of an implementation of the UpgradeLockable concept can be
downgraded to upgradable ownership or shared ownership, and upgradable
ownership can be downgraded to plain shared ownership.

A type L meets the UpgradeLockable requirements if
it meets the SharedLockable requirements and
the following expressions are well-formed and have the specified semantics.

Lock ownership acquired through a call to lock_upgrade()
must be released through a call to unlock_upgrade().
If the ownership type is changed through a call to one of the unlock_xxx_and_lock_yyy()
functions, ownership must be released through a call to the unlock function
corresponding to the new level of ownership.

If the tick period of rel_time
is not exactly convertible to the native tick period, the duration
shall be rounded up to the nearest native tick period. Attempts
to obtain upgrade lock ownership for the calling thread within
the relative timeout specified by rel_time.
If the time specified by rel_time
is less than or equal to rel_time.zero(), the function attempts to obtain
ownership without blocking (as if by calling try_lock_upgrade()). The function returns within
the timeout specified by rel_time
only if it has obtained upgrade ownership of the mutex object.

Returns:

true if upgrade ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has upgrade ownership of m.

The function attempts to obtain upgrade ownership of the mutex.
If abs_time has
already passed, the function attempts to obtain upgrade ownership
without blocking (as if by calling try_lock_upgrade()). The function returns before
the absolute timeout specified by abs_time
only if it has obtained upgrade ownership of the mutex object.

Returns:

true if upgrade ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has upgrade ownership of m.

The function attempts to atomically convert the ownership from
shared to exclusive for the calling thread without blocking. For
this conversion to be successful, this thread must be the only
thread holding any ownership of the lock. If the conversion is
not successful, the shared ownership of m is retained.

Returns:

true if exclusive
ownership was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has exclusive ownership of m.

If the tick period of rel_time
is not exactly convertible to the native tick period, the duration
shall be rounded up to the nearest native tick period. The function
attempts to atomically convert the ownership from shared to exclusive
for the calling thread within the relative timeout specified by
rel_time. If the
time specified by rel_time
is less than or equal to rel_time.zero(), the function attempts to obtain
exclusive ownership without blocking (as if by calling try_unlock_shared_and_lock()).
The function shall return within the timeout specified by rel_time only if it has obtained
exclusive ownership of the mutex object. For this conversion to
be successful, this thread must be the only thread holding any
ownership of the lock at the moment of conversion. If the conversion
is not successful, the shared ownership of the mutex is retained.

Returns:

true if exclusive
ownership was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has exclusive ownership of m.

The function attempts to atomically convert the ownership from
shared to exclusive for the calling thread within the absolute
timeout specified by abs_time.
If abs_time has
already passed, the function attempts to obtain exclusive ownership
without blocking (as if by calling try_unlock_shared_and_lock()). The function shall return before
the absolute timeout specified by abs_time
only if it has obtained exclusive ownership of the mutex object.
For this conversion to be successful, this thread must be the only
thread holding any ownership of the lock at the moment of conversion.
If the conversion is not successful, the shared ownership of the
mutex is retained.

Returns:

true if exclusive
ownership was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has exclusive ownership of m.

The function attempts to atomically convert the ownership from
shared to upgrade for the calling thread without blocking. For
this conversion to be successful, there must be no thread holding
upgrade ownership of this object. If the conversion is not successful,
the shared ownership of the mutex is retained.

Returns:

true if upgrade ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has upgrade ownership of m.

If the tick period of rel_time
is not exactly convertible to the native tick period, the duration
shall be rounded up to the nearest native tick period. The function
attempts to atomically convert the ownership from shared to upgrade
for the calling thread within the relative timeout specified by
rel_time. If the
time specified by rel_time
is less than or equal to rel_time.zero(), the function attempts to obtain
upgrade ownership without blocking (as if by calling try_unlock_shared_and_lock_upgrade()). The function shall return within
the timeout specified by rel_time
only if it has obtained exclusive ownership of the mutex object.
For this conversion to be successful, there must be no thread holding
upgrade ownership of this object at the moment of conversion. If
the conversion is not successful, the shared ownership of m is
retained.

Returns:

true if upgrade ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has upgrade ownership of m.

The function attempts to atomically convert the ownership from
shared to upgrade for the calling thread within the absolute timeout
specified by abs_time.
If abs_time has
already passed, the function attempts to obtain upgrade ownership
without blocking (as if by calling try_unlock_shared_and_lock_upgrade()). The function shall return before
the absolute timeout specified by abs_time
only if it has obtained upgrade ownership of the mutex object.
For this conversion to be successful, there must be no thread holding
upgrade ownership of this object at the moment of conversion. If
the conversion is not successful, the shared ownership of the mutex
is retained.

Returns:

true if upgrade ownership
was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has upgrade ownership of m.

The function attempts to atomically convert the ownership from
upgrade to exclusive for the calling thread without blocking. For
this conversion to be successful, this thread must be the only
thread holding any ownership of the lock. If the conversion is
not successful, the upgrade ownership of m is retained.

Returns:

true if exclusive
ownership was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has exclusive ownership of m.

If the tick period of rel_time
is not exactly convertible to the native tick period, the duration
shall be rounded up to the nearest native tick period. The function
attempts to atomically convert the ownership from upgrade to exclusive
for the calling thread within the relative timeout specified by
rel_time. If the
time specified by rel_time
is less than or equal to rel_time.zero(), the function attempts to obtain
exclusive ownership without blocking (as if by calling try_unlock_upgrade_and_lock()). The function shall return within
the timeout specified by rel_time
only if it has obtained exclusive ownership of the mutex object.
For this conversion to be successful, this thread shall be the
only thread holding any ownership of the lock at the moment of
conversion. If the conversion is not successful, the upgrade ownership
of m is retained.

Returns:

true if exclusive
ownership was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has exclusive ownership of m.

The function attempts to atomically convert the ownership from
upgrade to exclusive for the calling thread within the absolute
timeout specified by abs_time.
If abs_time has
already passed, the function attempts to obtain exclusive ownership
without blocking (as if by calling try_unlock_upgrade_and_lock()). The function shall return before
the absolute timeout specified by abs_time
only if it has obtained exclusive ownership of the mutex object.
For this conversion to be successful, this thread shall be the
only thread holding any ownership of the lock at the moment of
conversion. If the conversion is not successful, the upgrade ownership
of m is retained.

Returns:

true if exclusive
ownership was acquired for the current thread, false
otherwise.

Postcondition:

If the call returns true,
the current thread has exclusive ownership of m.

As the semantic "ensures that the associated mutex is locked during
the lifetime of the lock. " can not be described by syntactic requirements
a is_strict_lock_sur_parole
trait must be specialized by the user defining the lock so that the following
assertion is true:

boost::unique_lock is more complex than
boost::lock_guard: not only does it provide
for RAII-style locking, it also allows for deferring acquiring the lock
until the lock()
member function is called explicitly, or trying to acquire the lock in
a non-blocking fashion, or with a timeout. Consequently, unlock()
is only called in the destructor if the lock object has locked the Lockable object, or otherwise
adopted a lock on the Lockable object.

The member functions of boost::unique_lock are not thread-safe.
In particular, boost::unique_lock is intended to model
the ownership of a Lockable object by a particular
thread, and the member functions that release ownership of the lock state
(including the destructor) must be called by the same thread that acquired
ownership of the lock state.

Constructs an object of type boost::unique_lock. Let pm be the pointer to the mutex
and owns the ownership
state. Initializes pm
with nullptr and owns
with false. If sl.owns_lock()() returns false,
sets pm to the
return value of sl.release(). Else sl.owns_lock()() returns true,
and in this case if sl.mutex()->try_unlock_shared_and_lock() returns true,
sets pm to the
value returned by sl.release() and sets owns
to true.

Note:

If sl.owns_lock()
returns true and
sl.mutex()->try_unlock_shared_and_lock()
returns false, sl is not modified.

Throws:

Nothing.

Notes:

Available only if BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSION
and BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
is defined on Windows platform

Constructs an object of type boost::unique_lock, initializing
pm with nullptr and owns
with false. If sl.owns_lock()() returns false,
sets pm to the
return value of sl.release(). Else sl.owns_lock()() returns true,
and in this case if sl.mutex()->try_unlock_shared_and_lock_until(abs_time) returns true,
sets pm to the
value returned by sl.release() and sets owns
to true.

Constructs an object of type boost::unique_lock, initializing
pm with nullptr and owns
with false. If sl.owns_lock()() returns false,
sets pm to the
return value of sl.release(). Else sl.owns_lock() returns true,
and in this case if sl.mutex()->try_unlock_shared_and_lock_for(rel_time) returns true,
sets pm to the
value returned by sl.release() and sets owns
to true.

The association between *this and the Lockable object is removed,
without affecting the lock state of the Lockable object. If owns_lock()
would have returned true,
it is the responsibility of the calling code to ensure that the
Lockable is correctly
unlocked.

Returns:

A pointer to the Lockable object associated
with *this
at the point of the call, or NULL
if there is no such object.

Like boost::unique_lock, not only does it
provide for RAII-style locking, it also allows for deferring acquiring
the lock until the lock()
member function is called explicitly, or trying to acquire the lock in
a non-blocking fashion, or with a timeout. Consequently, unlock()
is only called in the destructor if the lock object has locked the Lockable object, or otherwise
adopted a lock on the Lockable object.

The member functions of boost::shared_lock are not thread-safe.
In particular, boost::shared_lock is intended to model
the shared ownership of a Lockable object by a particular
thread, and the member functions that release ownership of the lock state
(including the destructor) must be called by the same thread that acquired
ownership of the lock state.

The association between *this and the Lockable object is removed,
without affecting the lock state of the Lockable object. If owns_lock()
would have returned true,
it is the responsibility of the calling code to ensure that the
Lockable is correctly
unlocked.

Returns:

A pointer to the Lockable object associated
with *this
at the point of the call, or NULL
if there is no such object.

Like boost::unique_lock, not only does it
provide for RAII-style locking, it also allows for deferring acquiring
the lock until the lock()
member function is called explicitly, or trying to acquire the lock in
a non-blocking fashion, or with a timeout. Consequently, unlock()
is only called in the destructor if the lock object has locked the Lockable object, or otherwise
adopted a lock on the Lockable object.

The member functions of boost::upgrade_lock are not thread-safe.
In particular, boost::upgrade_lock is intended to model
the upgrade ownership of a UpgradeLockable object by a particular
thread, and the member functions that release ownership of the lock state
(including the destructor) must be called by the same thread that acquired
ownership of the lock state.

The member typedef scoped_try_lock
is provided for each distinct MutexType
as a typedef to a class with the preceding definition. The semantics of
each constructor and member function are identical to those of boost::unique_lock<MutexType>
for the same MutexType,
except that the constructor that takes a single reference to a mutex will
call m.try_lock() rather than m.lock().

strict_lock is the simplest
StrictLock: on construction
it acquires ownership of the implementation of the BasicLockable concept supplied
as the constructor parameter. On destruction, the ownership is released.
This provides simple RAII-style locking of a BasicLockable object, to facilitate
exception-safe locking and unlocking.

A nested strict lock is a scoped lock guard ensuring a mutex is locked
on its scope, by taking ownership of an nesting lock, locking the mutex
on construction if not already locked and restoring the ownership to
the nesting lock on destruction.

externally_locked is
a model of Lockable, it cloaks an object
of type T, and actually
provides full access to that object through the get and set member functions,
provided you pass a reference to a strict lock object.

externally_locked is
a model of Lockable, it cloaks an object
of type T, and actually
provides full access to that object through the get and set member functions,
provided you pass a reference to a strict lock object.

shared_lock_guard is very simple:
on construction it acquires shared ownership of the implementation of the
SharedLockable concept supplied
as the constructor parameter. On destruction, the ownership is released.
This provides simple RAII-style locking of a SharedLockable object, to facilitate
exception-safe shared locking and unlocking. In addition, the shared_lock_guard(SharedLockable&m,boost::adopt_lock_t) constructor allows the shared_lock_guard object to take
shared ownership of a lock already held by the current thread.

reverse_lock reverse the operations
of a lock: it provide for RAII-style, that unlocks the lock at construction
time and lock it at destruction time. In addition, it transfer ownership
temporarily, so that the mutex can not be locked using the Lock.

Locks the Lockable objects supplied
as arguments in an unspecified and indeterminate order in a way that
avoids deadlock. It is safe to call this function concurrently from
multiple threads with the same mutexes (or other lockable objects)
in different orders without risk of deadlock. If any of the lock()
or try_lock()
operations on the supplied Lockable objects throws
an exception any locks acquired by the function will be released
before the function exits.

Locks all the Lockable objects in the
supplied range in an unspecified and indeterminate order in a way
that avoids deadlock. It is safe to call this function concurrently
from multiple threads with the same mutexes (or other lockable objects)
in different orders without risk of deadlock. If any of the lock()
or try_lock()
operations on the Lockable objects in the
supplied range throws an exception any locks acquired by the function
will be released before the function exits.

Calls try_lock()
on each of the Lockable objects supplied
as arguments. If any of the calls to try_lock()
returns false then all
locks acquired are released and the zero-based index of the failed
lock is returned.

If any of the try_lock()
operations on the supplied Lockable objects throws
an exception any locks acquired by the function will be released
before the function exits.

Returns:

-1
if all the supplied Lockable objects are now
locked by the calling thread, the zero-based index of the object
which could not be locked otherwise.

Calls try_lock()
on each of the Lockable objects in the
supplied range. If any of the calls to try_lock()
returns false then all
locks acquired are released and an iterator referencing the failed
lock is returned.

If any of the try_lock()
operations on the supplied Lockable objects throws
an exception any locks acquired by the function will be released
before the function exits.

Returns:

end if all the supplied
Lockable objects are now
locked by the calling thread, an iterator referencing the object
which could not be locked otherwise.

Returns an instance of native_handle_type
that can be used with platform-specific APIs to manipulate the
underlying implementation. If no such instance exists, native_handle()
and native_handle_type
are not present.

Returns an instance of native_handle_type
that can be used with platform-specific APIs to manipulate the
underlying implementation. If no such instance exists, native_handle()
and native_handle_type
are not present.

Returns an instance of native_handle_type
that can be used with platform-specific APIs to manipulate the
underlying implementation. If no such instance exists, native_handle()
and native_handle_type
are not present.

Returns an instance of native_handle_type
that can be used with platform-specific APIs to manipulate the
underlying implementation. If no such instance exists, native_handle()
and native_handle_type
are not present.

Note the the lack of reader-writer priority policies in shared_mutex. This
is due to an algorithm credited to Alexander Terekhov which lets the OS
decide which thread is the next to get the lock without caring whether
a unique lock or shared lock is being sought. This results in a complete
lack of reader or writer starvation. It is simply fair.

The classes condition_variable
and condition_variable_any
provide a mechanism for one thread to wait for notification from another
thread that a particular condition has become true. The general usage pattern
is that one thread locks a mutex and then calls wait
on an instance of condition_variable
or condition_variable_any.
When the thread is woken from the wait, then it checks to see if the appropriate
condition is now true, and continues if so. If the condition is not true,
then the thread then calls wait
again to resume waiting. In the simplest case, this condition is just a boolean
variable:

Notice that the lock is passed
to wait: wait
will atomically add the thread to the set of threads waiting on the condition
variable, and unlock the mutex. When the thread is woken, the mutex will
be locked again before the call to wait
returns. This allows other threads to acquire the mutex in order to update
the shared data, and ensures that the data associated with the condition
is correctly synchronized.

In the mean time, another thread sets the condition to true,
and then calls either notify_one
or notify_all on the condition
variable to wake one waiting thread or all the waiting threads respectively.

Note that the same mutex is locked before the shared data is updated, but
that the mutex does not have to be locked across the call to notify_one.

This example uses an object of type condition_variable,
but would work just as well with an object of type condition_variable_any:
condition_variable_any is
more general, and will work with any kind of lock or mutex, whereas condition_variable requires that the lock
passed to wait is an instance
of boost::unique_lock<boost::mutex>.
This enables condition_variable
to make optimizations in some cases, based on the knowledge of the mutex
type; condition_variable_any
typically has a more complex implementation than condition_variable.

lock is locked
by the current thread, and either no other thread is currently
waiting on *this,
or the execution of the mutex() member function on the lock objects supplied in the
calls to wait or
timed_wait in all
the threads currently waiting on *this would return the same value
as lock->mutex()
for this call to wait.

Effects:

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
or spuriously. When the thread is unblocked (for whatever reason),
the lock is reacquired by invoking lock.lock() before the call to wait returns. The lock is also
reacquired by invoking lock.lock() if the function exits with an
exception.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

lock is locked
by the current thread, and either no other thread is currently
waiting on *this,
or the execution of the mutex() member function on the lock objects supplied in the
calls to wait or
timed_wait in all
the threads currently waiting on *this would return the same value
as lock->mutex()
for this call to wait.

Effects:

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
when the time as reported by boost::get_system_time() would be equal to or later than
the specified abs_time,
or spuriously. When the thread is unblocked (for whatever reason),
the lock is reacquired by invoking lock.lock() before the call to wait returns. The lock is also
reacquired by invoking lock.lock() if the function exits with an
exception.

Returns:

false if the call
is returning because the time specified by abs_time
was reached, true
otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

lock is locked
by the current thread, and either no other thread is currently
waiting on *this,
or the execution of the mutex() member function on the lock objects supplied in the
calls to wait or
timed_wait in all
the threads currently waiting on *this would return the same value
as lock->mutex()
for this call to wait.

Effects:

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
after the period of time indicated by the rel_time
argument has elapsed, or spuriously. When the thread is unblocked
(for whatever reason), the lock is reacquired by invoking lock.lock()
before the call to wait
returns. The lock is also reacquired by invoking lock.lock()
if the function exits with an exception.

Returns:

false if the call
is returning because the time period specified by rel_time has elapsed, true otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Note

The duration overload of timed_wait is difficult to use correctly.
The overload taking a predicate should be preferred in most cases.

lock is locked
by the current thread, and either no other thread is currently
waiting on *this,
or the execution of the mutex() member function on the lock objects supplied in the
calls to wait or
wait_for or wait_until in all the threads
currently waiting on *this would return the same value
as lock->mutex()
for this call to wait.

Effects:

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
when the time as reported by Clock::now() would be equal to or later than
the specified abs_time,
or spuriously. When the thread is unblocked (for whatever reason),
the lock is reacquired by invoking lock.lock() before the call to wait returns. The lock is also
reacquired by invoking lock.lock() if the function exits with an
exception.

Returns:

cv_status::timeout if the call is returning
because the time specified by abs_time
was reached, cv_status::no_timeout
otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

lock is locked
by the current thread, and either no other thread is currently
waiting on *this,
or the execution of the mutex() member function on the lock objects supplied in the
calls to wait or
wait_until or
wait_for in all
the threads currently waiting on *this would return the same value
as lock->mutex()
for this call to wait.

Effects:

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
after the period of time indicated by the rel_time
argument has elapsed, or spuriously. When the thread is unblocked
(for whatever reason), the lock is reacquired by invoking lock.lock()
before the call to wait
returns. The lock is also reacquired by invoking lock.lock()
if the function exits with an exception.

Returns:

cv_status::timeout if the call is returning
because the time period specified by rel_time
has elapsed, cv_status::no_timeout otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Note

The duration overload of timed_wait is difficult to use correctly.
The overload taking a predicate should be preferred in most cases.

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
or spuriously. When the thread is unblocked (for whatever reason),
the lock is reacquired by invoking lock.lock() before the call to wait returns. The lock is also
reacquired by invoking lock.lock() if the function exits with an
exception.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
when the time as reported by boost::get_system_time() would be equal to or later than
the specified abs_time,
or spuriously. When the thread is unblocked (for whatever reason),
the lock is reacquired by invoking lock.lock() before the call to wait returns. The lock is also
reacquired by invoking lock.lock() if the function exits with an
exception.

Returns:

false if the call
is returning because the time specified by abs_time
was reached, true
otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
after the period of time indicated by the rel_time
argument has elapsed, or spuriously. When the thread is unblocked
(for whatever reason), the lock is reacquired by invoking lock.lock()
before the call to wait
returns. The lock is also reacquired by invoking lock.lock()
if the function exits with an exception.

Returns:

false if the call
is returning because the time period specified by rel_time has elapsed, true otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Note

The duration overload of timed_wait is difficult to use correctly.
The overload taking a predicate should be preferred in most cases.

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
when the time as reported by Clock::now() would be equal to or later than
the specified abs_time,
or spuriously. When the thread is unblocked (for whatever reason),
the lock is reacquired by invoking lock.lock() before the call to wait returns. The lock is also
reacquired by invoking lock.lock() if the function exits with an
exception.

Returns:

cv_status::timeout if the call is returning
because the time specified by abs_time
was reached, cv_status::no_timeout
otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Atomically call lock.unlock() and blocks the current thread.
The thread will unblock when notified by a call to this->notify_one()
or this->notify_all(),
after the period of time indicated by the rel_time
argument has elapsed, or spuriously. When the thread is unblocked
(for whatever reason), the lock is reacquired by invoking lock.lock()
before the call to wait
returns. The lock is also reacquired by invoking lock.lock()
if the function exits with an exception.

Returns:

cv_status::timeout if the call is returning
because the time specified by abs_time
was reached, cv_status::no_timeout
otherwise.

Postcondition:

lock is locked
by the current thread.

Throws:

boost::thread_resource_error if an error
occurs. boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Note

The duration overload of timed_wait is difficult to use correctly.
The overload taking a predicate should be preferred in most cases.

lk is locked by the
calling thread and either no other thread is waiting on cond, or lk.mutex() returns the same value for each
of the lock arguments supplied by all concurrently waiting (via
wait, wait_for, or wait_until)
threads.

Effects:

transfers ownership of the lock associated with lk
into internal storage and schedules cond
to be notified when the current thread exits, after all objects of
thread storage duration associated with the current thread have been
destroyed. This notification shall be as if

Function and each
or the ArgTypes are
MoveConstructible
and invoke(decay_copy(boost::forward<Function>(f)),decay_copy(boost::forward<ArgTypes>(args))...)
shall be well formed.

Effects:

Calls to call_once
on the same once_flag
object are serialized. If there has been no prior effective call_once on the same once_flag object, the argument
func is called as-if
by invoking invoke(decay_copy(boost::forward<Function>(f)),decay_copy(boost::forward<ArgTypes>(args))...), and the invocation of call_once is effective if and only
if invoke(decay_copy(boost::forward<Function>(f)),decay_copy(boost::forward<ArgTypes>(args))...)
returns without exception. If an exception is thrown, the exception
is propagated to the caller. If there has been a prior effective
call_once on the
same once_flag object,
the call_once returns
without invoking func.

Synchronization:

The completion of an effective call_once
invocation on a once_flag
object, synchronizes with all subsequent call_once
invocations on the same once_flag
object.

Throws:

thread_resource_error
when the effects cannot be achieved or any exception propagated from
func.

Note:

The function passed to call_once
must not also call call_once
passing the same once_flag
object. This may cause deadlock, or invoking the passed function
a second time. The alternative is to allow the second call to return
immediately, but that assumes the code knows it has been called recursively,
and can proceed even though the call to call_once
didn't actually call the function, in which case it could also avoid
calling call_once
recursively.

Note:

On some compilers this function has some restrictions, e.g. if variadic
templates are not supported the number of arguments is limited to
3; .

voidcall_once(void(*func)(),once_flag&flag);

This second overload is provided for backwards compatibility and is deprecated.
The effects of call_once(func,flag) shall be the same as those of call_once(flag,func).

A barrier is a simple concept. Also known as a rendezvous,
it is a synchronization point between multiple threads. The barrier is configured
for a particular number of threads (n),
and as threads reach the barrier they must wait until all n
threads have arrived. Once the n-th
thread has reached the barrier, all the waiting threads can proceed, and
the barrier is reset.

Block until count
threads have called wait
or count_down_and_wait
on *this.
When the count-th
thread calls wait,
the barrier is reset and all waiting threads are unblocked. The
reset depends on whether the barrier was constructed with a completion
function or not. If there is no completion function or if the completion
function result is void, the reset consists in restoring the original
count. Otherwise the rest consist in assigning the result of the
completion function (which must not be 0).

Returns:

true for exactly one
thread from each batch of waiting threads, false
otherwise.

Throws:

- boost::thread_resource_error if an error
occurs.

- boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Block until count
threads have called wait
or count_down_and_wait
on *this.
When the count-th
thread calls wait,
the barrier is reset and all waiting threads are unblocked. The
reset depends on whether the barrier was constructed with a completion
function or not. If there is no completion function or if the completion
function result is void, the reset consists in restoring the original
count. Otherwise the rest consist in assigning the result of the
completion function (which must not be 0).

Throws:

- boost::thread_resource_error if an error
occurs.

- boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

Setting multiple threads to perform a task, and then waiting until
all threads have reached a common point.

Creating multiple threads, which wait for a signal before advancing
beyond a common point.

An example of the first use case would be as follows:

voidDoWork(thread_pool*pool){latchcompletion_latch(NTASKS);for(inti=0;i<NTASKS;++i){pool->submit([&]{// perform work...completion_latch.count_down();}));}// Block until work is donecompletion_latch.wait();}

An example of the second use case is shown below. We need to load data
and then process it using a number of threads. Loading the data is I/O
bound, whereas starting threads and creating data structures is CPU bound.
By running these in parallel, throughput can be increased.

Decrements the internal count by 1. If the resulting count is not
0, blocks the calling thread until the internal count is decremented
to 0 by one or more other threads calling count_down() or count_down_and_wait().

Throws:

- boost::thread_resource_error if an error
occurs.

- boost::thread_interrupted if the wait
was interrupted by a call to interrupt()
on the boost::thread object associated
with the current thread of execution.

These features are experimental and subject to change in future versions.
There are not too much tests yet, so it is possible that you can find out
some trivial bugs :(

Note

These features are based on the N3785 - Executors and Schedulers revision 3
C++1y proposal from Chris Mysen, Niklas Gustafsson, Matt Austern, Jeffrey
Yasskin. The text that follows has been adapted from tis paper to show
the differences.

Executors are objects that can execute units of work packaged as function
objects. Boost.Thread differs from N3785 mainly in the an Executor doesn't
needs to inherit from an abstract class Executor. Static polymorphism is
used instead and type erasure is used internally.

Multithreaded programs often involve discrete (sometimes small) units of
work that are executed asynchronously. This often involves passing work
units to some component that manages execution. We already have boost::async,
which potentially executes a function asynchronously and eventually returns
its result in a future. (“As if” by launching a new thread.)

If there is a regular stream of small work items then we almost certainly
don’t want to launch a new thread for each, and it’s likely that we
want at least some control over which thread(s) execute which items. It
is often convenient to represent that control as multiple executor objects.
This allows programs to start executors when necessary, switch from one
executor to another to control execution policy, and use multiple executors
to prevent interference and thread exhaustion. Several possible implementations
exist of the executor class and in practice there are a number of main
groups of executors which have been found to be useful in real-world code
(more implementations exist, this is simply a high level classification
of them). These differ along a couple main dimensions, how many execution
contexts will be used, how they are selected, and how they are prioritized.

Thread Pools

Simple unbounded thread pool, which can queue up an unbounded
amount of work and maintains a dedicated set of threads (up to
some maximum) which dequeue and execute work as available.

Bounded thread pools, which can be implemented as a specialization
of the previous ones with a bounded queue or semaphore, which
limits the amount of queuing in an attempt to bound the time
spent waiting to execute and/or limit resource utilization for
work tasks which hold state which is expensive to hold.

Thread-spawning executors, in which each work always executes
in a new thread.

Prioritized thread pools, which have works which are not equally
prioritized such that work can move to the front of the execution
queue if necessary. This requires a special comparator or prioritization
function to allow for work ordering and normally is implemented
as a blocking priority queue in front of the pool instead of
a blocking queue. This has many uses but is a somewhat specialized
in nature and would unnecessarily clutter the initial interface.

Work stealing thread pools, this is a specialized use case and
is encapsulated in the ForkJoinPool in java, which allows lightweight
work to be created by tasks in the pool and either run by the
same thread for invocation efficiency or stolen by another thread
without additional work. These have been left out until there
is a more concrete fork-join proposal or until there is a more
clear need as these can be complicated to implement

Mutual exclusion executors

Serial executors, which guarantee all work to be executed such
that no two works will execute concurrently. This allows for
a sequence of operations to be queued in sequence and that sequential
order is maintained and work can be queued on a separate thread
but with no mutual exclusion required.

Loop executor, in which one thread donates itself to the executor
to execute all queued work. This is related to the serial executor
in that it guarantees mutual exclusion, but instead guarantees
a particular thread will execute the work. These are particularly
useful for testing purposes where code assumes an executor but
testing code desires control over execution.

GUI thread executor, where a GUI framework can expose an executor
interface to allow other threads to queue up work to be executed
as part of the GUI thread. This behaves similarly to a loop executor,
but must be implemented as a custom interface as part of the
framework.

Inline executors, which execute inline to the thread which calls submit().
This has no queuing and behaves like a normal executor, but always
uses the caller’s thread to execute. This allows parallel execution
of works, though. This type of executor is often useful when there
is an executor required by an interface, but when for performance reasons
it’s better not to queue work or switch threads. This is often very
useful as an optimization for work continuations which should execute
immediately or quickly and can also be useful for optimizations when
an interface requires an executor but the work tasks are too small
to justify the overhead of a full thread pool.

A question arises of which of these executors (or others) be included in
this library. There are use cases for these and many other executors. Often
it is useful to have more than one implemented executor (e.g. the thread
pool) to have more precise control of where the work is executed due to
the existence of a GUI thread, or for testing purposes. A few core executors
are frequently useful and these have been outlined here as the core of
what should be in this library, if common use cases arise for alternative
executor implementations, they can be added in the future. The current
set provided here are: a basic thread pool basic_thread_pool,
a serial executor serial_executor,
a loop executor loop_executor,
an inline executor inline_executor
and a thread-spawning executor thread_executor.

The authors of Boost.Thread have taken a different approach respect to
N3785. Instead of basing all the design on a abstract executor class we
make executor concepts. We believe that this is the good direction as a
static polymorphic executor can be seen as a dynamic polymorphic executor
using a simple adaptor. We believe also that it would make the library
more usable, and more convenient for users.

The major design decisions concern deciding what a unit of work is, how
to manage with units of work and time related functions in a polymorphic
way.

An Executor is an object that schedules the closures that have been submitted
to it, usually asynchronously. There could be multiple models of the Executor
class. Some specific design notes:

Thread pools are well know models of the Executor concept, and this
library does indeed include a basic_thread_pool class, but other implementations
also exist, including the ability to schedule work on GUI threads,
scheduling work on a donor thread, as well as several specializations
of thread pools.

The choice of which executor to use is explicit. This is important
for reasons described in the Motivation section. In particular, consider
the common case of an asynchronous operation that itself spawns asynchronous
operations. If both operations ran on the same executor, and if that
executor had a bounded number of worker threads, then we could get
deadlock. Programs often deal with such issues by splitting different
kinds of work between different executors.

Even if there could be a strong value in having a default executor,
that can be used when detailed control is unnecessary, the authors
don't know how to implement it in a portable and robust way.

The library provides Executors based on static and dynamic polymorphism.
The static polymorphism interface is intended to be used on contexts
that need to have the best performances. The dynamic polymorphism interface
has the advantage to been able to change the executor a function is
using without making it a template and is possible to pass executors
across a binary interface. For some applications, the cost of an additional
virtual dispatch could be almost certainly negligible compared to the
other operations involved.

Conceptually, an executor puts closures on a queue and at some point
executes them. The queue is always unbounded, so adding a closure to
an executor never blocks. (Defining “never blocks” formally is
challenging, but informally we just mean that submit() is an ordinary
function that executes something and returns, rather than waiting for
the completion of some potentially long running operation in another
thread.)

One important question is just what a closure is. This library has a very
simple answer: a closure is a Callable
with no parameters and returning void.

N3785 choose the more specific std::function<void()> as it provides only dynamic polymorphism
and states that in practice the implementation of a template based approach
or another approach is impractical. The authors of this library think that
the template based approach is compatible with a dynamic based approach.
They give some arguments:

The first one is that a virtual function can not be a template. This is
true but it is also true that the executor interface can provide the template
functions that call to the virtual public functions. Another reason they
give is that "a template parameter would complicate the interface
without adding any real generality. In the end an executor class is going
to need some kind of type erasure to handle all the different kinds of
function objects with void() signature, and that’s exactly what
std::function already does". We think that it is up to the executor
to manage with this implementation details, not to the user.

We share all the argument they give related to the void() interface of the work unit. A work unit
is a closure that takes no arguments and returns no value. This is indeed
a limitation on user code, but combined with boost::async
taking executors as parameters the user has all what she needs.

The third one is related to performance. They assert that "any mechanism
for storing closures on an executor’s queue will have to use some form
of type erasure. There’s no reason to believe that a custom closure mechanism,
written just for std::executor and used nowhere else within the standard
library, would be better in that respect than std::function<void()>". We believe that the implementation
can do better that storing the closure on a std::function<void()>. e.g. the implementation can use
intrusive data to store the closure and the pointers to other nodes needed
to store the closures in a given order.

In addition std::function<void()>
can not be constructed by moving the closure, so e.g. std::packaged_task
could not be a Closure.

As in N3785 and based on the same design decision than std/boost::thread if a user closure throws an exception,
the executor must call the std::terminate
function. Note that when we combine boost::async
and Executors, the exception
will be caught by the closure associated to the returned future, so that
the exception is stored on the returned future, as for the other async overloads.

It is common idiom to set some thread local variable at the beginning of
a thread. As Executors could instantiate threads internally these Executors
shall have the ability to call a user specific function at thread entry
on the executor constructor.

For executors that don't instantiate any thread and that would use the
current thread this function shall be called only for the thread calling
the at_thread_entry member
function.

The library authors share some of the concerns of the C++ standard committee
(introduction of a new single shared resource, a singleton, could make
it difficult to make it portable to all the environments) and that this
library doesn't need to provide a default executor for the time been.

The user can always define his default executor himself and use the at_thread_entry member function to set
the default constructor.