mirror of
https://github.com/boostorg/thread.git
synced 2026-01-24 18:32:32 +00:00
484 lines
21 KiB
XML
484 lines
21 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!DOCTYPE library PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
|
|
"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd" [
|
|
<!ENTITY % threads.entities SYSTEM "entities.xml">
|
|
%threads.entities;
|
|
]>
|
|
<section id="threads.concepts" last-revision="$Date$">
|
|
<title>Concepts</title>
|
|
<section id="threads.concepts.mutexes">
|
|
<title>Mutexes</title>
|
|
<para>A mutex (short for mutual-exclusion) object is used to serializes
|
|
access to a resource shared between multiple threads. The
|
|
<link linkend="threads.concepts.Mutex">Mutex</link> concept, with
|
|
<link linkend="threads.concepts.TryMutex">TryMutex</link> and
|
|
<link linkend="threads.concepts.TimedMutex">TimedMutex</link> refinements,
|
|
formalize the requirements. A model that implements Mutex and its
|
|
refinements has two states: <emphasis role="bold">locked</emphasis> and
|
|
<emphasis role="bold">unlocked</emphasis>. Before using a shared resource, a
|
|
thread locks a <emphasis role="bold">Boost.Threads</emphasis> mutex object
|
|
(an object whose type is a model of
|
|
<link linkend="threads.concepts.Mutex">Mutex</link> or one of it's
|
|
refinements), ensuring
|
|
<link linkend="threads.glossary.thread-safe">thread-safe</link> access to
|
|
the shared resource. When use of the shared resource is complete, the thread
|
|
unlocks the mutex object, allowing another thread to acquire the lock and
|
|
use the shared resource.</para>
|
|
<para>Traditional C thread APIs, like POSIX threads or the Windows thread
|
|
APIs, expose functions to lock and unlock a mutex object. This is dangerous
|
|
since it's easy to forget to unlock a locked mutex. When the flow of control
|
|
is complex, with multiple return points, the likelihood of forgetting to
|
|
unlock a mutex object would become even greater. When exceptions are thrown,
|
|
it becomes nearly impossible to ensure that the mutex object is unlocked
|
|
properly when using these traditional API's. The result is
|
|
<link linkend="threads.glossary.deadlock">deadlock</link>.</para>
|
|
<para>Many C++ threading libraries use a pattern known as <emphasis>Scoped
|
|
Locking</emphasis> &cite.SchmidtStalRohnertBuschmann; to free the programmer from
|
|
the need to explicitly lock and unlock mutex objects. With this pattern, a
|
|
<link linkend="threads.concepts.locks">Lock</link> concept is employed where
|
|
the lock object's constructor locks the associated mutex object and the
|
|
destructor automatically does the unlocking. The
|
|
<emphasis role="bold">Boost.Threads</emphasis> library takes this pattern to
|
|
the extreme in that Lock concepts are the only way to lock and unlock a
|
|
mutex object: lock and unlock functions are not exposed by any
|
|
<emphasis role="bold">Boost.Threads</emphasis> mutex objects. This helps to
|
|
ensure safe usage patterns, especially when code throws exceptions.</para>
|
|
</section>
|
|
<section id="threads.concepts.locking-strategies">
|
|
<title>Locking Strategies</title>
|
|
<para>Every mutex object follows one of several locking strategies. These
|
|
strategies define the semantics for the locking operation when the calling
|
|
thread already owns a lock on the mutex object.</para>
|
|
<section id="threads.concepts.recursive-locking-strategy">
|
|
<title>Recursive Locking Strategy</title>
|
|
<para>With a recursive locking strategy when a thread attempts to acquire
|
|
a lock on the mutex object for which it already owns a lock, the operation
|
|
is successful. Note the distinction between a thread, which may have
|
|
multiple locks outstanding on a recursive mutex object, and a lock object,
|
|
which even for a recursive mutex object cannot have any of its lock
|
|
functions called multiple times without first calling unlock.</para>
|
|
<para>Internally a lock count is maintained and the owning thread must
|
|
unlock the mutex model the same number of times that it's locked it before
|
|
the mutex object's state returns to unlocked. Since mutex objects in
|
|
<emphasis role="bold">Boost.Threads</emphasis> expose locking
|
|
functionality only through lock concepts, a thread will always unlock a
|
|
mutex object the same number of times that it locked it. This helps to
|
|
eliminate a whole set of errors typically found in traditional C style
|
|
thread APIs.</para>
|
|
<para>Classes <classname>boost::recursive_mutex</classname>,
|
|
<classname>boost::recursive_try_mutex</classname> and
|
|
<classname>boost::recursive_timed_mutex</classname> use this locking
|
|
strategy.</para>
|
|
</section>
|
|
<section id="threads.concepts.checked-locking-strategy">
|
|
<title>Checked Locking Strategy</title>
|
|
<para>With a checked locking strategy when a thread attempts to acquire a
|
|
lock on the mutex object for which the thread already owns a lock, the
|
|
operation will fail with some sort of error indication. Further, attempts
|
|
by a thread to unlock a mutex object that was not locked by the thread
|
|
will also return some sort of error indication. In
|
|
<emphasis role="bold">Boost.Threads</emphasis>, an exception of type
|
|
<classname>boost::lock_error</classname> would be thrown in these cases.</para>
|
|
<para><emphasis role="bold">Boost.Threads</emphasis> does not currently
|
|
provide any mutex objects that use this strategy.</para>
|
|
</section>
|
|
<section id="threads.concepts.unchecked-locking-strategy">
|
|
<title>Unchecked Locking Strategy</title>
|
|
<para>With an unchecked locking strategy when a thread attempts to acquire
|
|
a lock on a mutex object for which the thread already owns a lock the
|
|
operation will
|
|
<link linkend="threads.glossary.deadlock">deadlock</link>. In general
|
|
this locking strategy is less safe than a checked or recursive strategy,
|
|
but it's also a faster strategy and so is employed by many libraries.</para>
|
|
<para><emphasis role="bold">Boost.Threads</emphasis> does not currently
|
|
provide any mutex objects that use this strategy.</para>
|
|
</section>
|
|
<section id="threads.concepts.unspecified-locking-strategy">
|
|
<title>Unspecified Locking Strategy</title>
|
|
<para>With an unspecified locking strategy, when a thread attempts to
|
|
acquire a lock on a mutex object for which the thread already owns a lock
|
|
the operation results in <emphasis role="bold">undefined
|
|
behavior</emphasis>.</para>
|
|
<para>In general a mutex object with an unspecified locking strategy is
|
|
unsafe, and it requires programmer discipline to use the mutex object
|
|
properly. However, this strategy allows an implementation to be as fast as
|
|
possible with no restrictions on its implementation. This is especially
|
|
true for portable implementations that wrap the native threading support
|
|
of a platform. For this reason, the classes
|
|
<classname>boost::mutex</classname>,
|
|
<classname>boost::try_mutex</classname> and
|
|
<classname>boost::timed_mutex</classname> use this locking strategy
|
|
despite the lack of safety.</para>
|
|
</section>
|
|
</section>
|
|
<section id="threads.concepts.sheduling-policies">
|
|
<title>Scheduling Policies</title>
|
|
<para>Every mutex object follows one of several scheduling policies. These
|
|
policies define the semantics when the mutex object is unlocked and there is
|
|
more than one thread waiting to acquire a lock. In other words, the policy
|
|
defines which waiting thread shall acquire the lock.</para>
|
|
<section id="threads.concepts.FIFO-scheduling-policy">
|
|
<title>FIFO Scheduling Policy</title>
|
|
<para>With a FIFO scheduling policy, threads waiting for the lock will
|
|
acquire it in a first come first serve order (or First In First Out). This
|
|
can help prevent a high priority thread from starving lower priority
|
|
threads that are also waiting on the mutex object's lock.</para>
|
|
</section>
|
|
<section id="threads.concepts.priority-driven-policy">
|
|
<title>Priority Driven Policy</title>
|
|
<para>With a Priority Driven scheduling policy, the thread with the
|
|
highest priority acquires the lock. Note that this means that low-priority
|
|
threads may never acquire the lock if the mutex object has high contention
|
|
and there is always at least one high-priority thread waiting. This is
|
|
known as thread starvation. When multiple threads of the same priority are
|
|
waiting on the mutex object's lock one of the other scheduling priorities
|
|
will determine which thread shall acquire the lock.</para>
|
|
</section>
|
|
<section id="threads.concepts.unspecified-policy">
|
|
<title>Unspecified Policy</title>
|
|
<para>The mutex object does not specify a scheduling policy. In order to
|
|
ensure portability, all <emphasis role="bold">Boost.Threads</emphasis>
|
|
mutex models use an unspecified scheduling policy.</para>
|
|
</section>
|
|
</section>
|
|
<section id="threads.concepts.requirements">
|
|
<title>Concept Requirements</title>
|
|
<section id="threads.concepts.Mutex">
|
|
<title>Mutex Concept</title>
|
|
<para>A Mutex object has two states: locked and unlocked. Mutex object
|
|
state can only be determined by an object meeting the
|
|
<link linkend="threads.concepts.ScopedLock">ScopedLock</link> requirements
|
|
and constructed for the Mutex object.</para>
|
|
<para>A Mutex is
|
|
<ulink url="../../utility/utility.htm#Class%20noncopyable">
|
|
NonCopyable</ulink>.</para>
|
|
<para>For a Mutex type M and an object m of that type, the following
|
|
expressions must be well-formed and have the indicated effects.</para>
|
|
<informaltable>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Expression</entry>
|
|
<entry>Effects</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>M m;</entry>
|
|
<entry>Constructs a mutex object m. Postcondition: m is
|
|
unlocked.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>(&->~M();</entry>
|
|
<entry>Precondition: m is unlocked. Destroys a mutex object
|
|
m.</entry>
|
|
</row>
|
|
<row>
|
|
<entry>M::scoped_lock</entry>
|
|
<entry>A model of
|
|
<link linkend="threads.concepts.ScopedLock">ScopedLock</link>
|
|
</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
</section>
|
|
<section id="threads.concepts.TryMutex">
|
|
<title>TryMutex Concept</title>
|
|
<para>A TryMutex is a refinement of
|
|
<link linkend="threads.concepts.Mutex">Mutex</link>. For a TryMutex type M
|
|
and an object m of that type, the following expressions must be
|
|
well-formed and have the indicated effects.</para>
|
|
<informaltable>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Expression</entry>
|
|
<entry>Effects</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>M::scoped_try_lock</entry>
|
|
<entry>A model of
|
|
<link linkend="threads.concepts.ScopedTryLock">ScopedTryLock</link>
|
|
</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
</section>
|
|
<section id="threads.concepts.TimedMutex">
|
|
<title>TimedMutex Concept</title>
|
|
<para>A TimedMutex is a refinement of
|
|
<link linkend="threads.concepts.TryMutex">TryMutex</link>. For a TimedMutex
|
|
type M and an object m of that type, the following expressions must be
|
|
well-formed and have the indicated effects.</para>
|
|
<informaltable>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Expression</entry>
|
|
<entry>Effects</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>M::scoped_timed_lock</entry>
|
|
<entry>A model of
|
|
<link
|
|
linkend="threads.concepts.ScopedTimedLock">ScopedTimedLock</link>
|
|
</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</informaltable>
|
|
</section>
|
|
</section>
|
|
<section id="threads.concepts.mutex-models">
|
|
<title>Mutex Models</title>
|
|
<para><emphasis role="bold">Boost.Threads</emphasis> currently supplies six
|
|
models of Mutex.</para>
|
|
<table>
|
|
<title>Mutex Models</title>
|
|
<tgroup cols="3"/>
|
|
<thead>
|
|
<row>
|
|
<entry>Concept</entry>
|
|
<entry>Refines</entry>
|
|
<entry>Models</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry><link linkend="threads.concepts.Mutex">Mutex</link></entry>
|
|
<entry></entry>
|
|
<entry><classname>boost::mutex</classname>
|
|
<classname>boost::recursive_mutex</classname></entry>
|
|
</row>
|
|
<row>
|
|
<entry><link
|
|
linkend="threads.concepts.TryMutex">TryMutex</link></entry>
|
|
<entry><link linkend="threads.concepts.Mutex">Mutex</link></entry>
|
|
<entry><classname>boost::try_mutex</classname>
|
|
<classname>boost::recursive_try_mutex</classname></entry>
|
|
</row>
|
|
<row>
|
|
<entry><link
|
|
linkend="threads.concepts.TimedMutex">TimedMutex</link></entry>
|
|
<entry><link
|
|
linkend="threads.concepts.TryMutex">TryMutex</link></entry>
|
|
<entry><classname>boost::timed_mutex</classname>
|
|
<classname>boost::recursive_timed_mutex</classname></entry>
|
|
</row>
|
|
</tbody>
|
|
</table>
|
|
</section>
|
|
<section id="threads.concepts.locks">
|
|
<title>Locks</title>
|
|
<para>A lock object provides a safe means for locking and unlocking a mutex
|
|
object (an object whose type is a model of <link
|
|
linkend="threads.concepts.Mutex">Mutex</link> or one of its refinements). In
|
|
other words they are an implementation of the <emphasis>Scoped
|
|
Locking</emphasis> &cite.SchmidtStalRohnertBuschmann; pattern. The <link
|
|
linkend="threads.concepts.ScopedLock">ScopedLock</link> concept, with <link
|
|
linkend="threads.concepts.ScopedTryLock">ScopedTryLock</link> and <link
|
|
linkend="threads.concepts.ScopedTimedLock">ScopedTimedLock</link>
|
|
refinements, formalize the requirements.</para>
|
|
<para>Lock objects are constructed with a reference to a mutex object and
|
|
typically acquire ownership of the mutex object by setting its state to
|
|
locked. They also ensure ownership is relinquished in the destructor. Lock
|
|
objects also expose functions to query the lock status and to manually lock
|
|
and unlock the mutex object.</para>
|
|
<para>Lock objects are meant to be short lived, expected to be used at block
|
|
scope only. The lock objects are not <link
|
|
linkend="threads.glossary.thread-safe">thread-safe</link>. Lock objects must
|
|
maintain state to indicate whether or not they've been locked and this state
|
|
is not protected by any synchronization concepts. For this reason a lock
|
|
object should never be shared between multiple threads.</para>
|
|
</section>
|
|
<section id="threads.concepts.lock-requirements">
|
|
<title>Lock Concept Requirements</title>
|
|
<section id="threads.concepts.Lock">
|
|
<title>Lock Concept</title>
|
|
<para>For a Lock type <code>L</code> and an object <code>lk</code> and
|
|
const object <code>clk</code> of that type, the following expressions must
|
|
be well-formed and have the indicated effects.
|
|
<table><title>Lock Expressions</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Expression</entry>
|
|
<entry>Effects</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry><code>(&lk)->~L();</code></entry>
|
|
<entry><code>if (locked()) unlock();</code></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>(&clk)->operator const void*()</code></entry>
|
|
<entry>Returns type void*, non-zero if if the associated mutex
|
|
object has been locked by <code>clk</code>, otherwise 0.</entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>clk.locked()</code></entry>
|
|
<entry>Returns a <code>bool</code>, <code>(&clk)->operator
|
|
const void*() != 0</code></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>lk.lock()</code></entry>
|
|
<entry><para>Throws <code>boost::lock_error</code> if <code>locked()</code>. If
|
|
the associated mutex object is already locked by some other
|
|
thread, places the current thread in the <link
|
|
linkend="threads.glossary.thread-state">Blocked</link> state until
|
|
the associated mutex is unlocked, after which the current thread
|
|
is placed in the <link
|
|
linkend="threads.glossary.thread-state">Ready</link> state,
|
|
eventually to be returned to the <link
|
|
linkend="threads.glossary.thread-state">Running</link> state. If
|
|
the associated mutex object is already locked by the same thread
|
|
the behavior is dependent on the <link
|
|
linkend="threads.concepts.locking-strategies">locking
|
|
strategy</link> of the associated mutex object.</para>
|
|
<para>Postcondition: <code>locked() == true</code></para></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>lk.unlock()</code></entry>
|
|
<entry><para>If <code>!locked()</code>, throws
|
|
<code>boost::lock_error</code>, otherwise unlocks the associated
|
|
mutex.</para>
|
|
<para>Postcondition: <code>!locked()</code></para></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table></para>
|
|
</section>
|
|
<section id="threads.concepts.ScopedLock">
|
|
<title>ScopedLock Concept</title>
|
|
<para>A ScopedLock is a refinement of <link
|
|
linkend="threads.concepts.Lock">Lock</link>. For a ScopedLock type
|
|
<code>L</code> and an object <code>lk</code> of that type, and an object
|
|
<code>m</code> of a type meeting the <link
|
|
linkend="threads.concepts.Mutex">Mutex</link> requirements, and an object
|
|
<code>b</code> of type <code>bool</code>, the following expressions must
|
|
be well-formed and have the indicated effects.
|
|
<table><title>ScopedLock Expressions</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Expression</entry>
|
|
<entry>Effects</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry><code>L lk(m);</code></entry>
|
|
<entry>Constructs an object <code>lk</code>, and associates mutex
|
|
object <code>m</code> with it, then calls
|
|
<code>lock()</code></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>L lk(m,b);</code></entry>
|
|
<entry>Constructs an object <code>lk</code>, and associates mutex
|
|
object <code>m</code> with it, then if <code>b</code>, calls
|
|
<code>lock()</code></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table></para>
|
|
</section>
|
|
<section id="threads.concepts.ScopedTryLock">
|
|
<title>ScopedTryLock Expressions</title>
|
|
<para>A ScopedTryLock is a refinement of <link
|
|
linkend="threads.concepts.Lock">Lock</link>. For a ScopedTryLock type
|
|
<code>L</code> and an object <code>lk</code> of that type, and an object
|
|
<code>m</code> of a type meeting the <link
|
|
linkend="threads.concepts.TryMutex">TryMutex</link> requirements, and an
|
|
object <code>b</code> of type <code>bool</code>, the following expressions
|
|
must be well-formed and have the indicated effects.
|
|
<table><title>ScopedTryLock Concept</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Expression</entry>
|
|
<entry>Effects</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry><code>L lk(m);</code></entry>
|
|
<entry>Constructs an object <code>lk</code>, and associates mutex
|
|
object <code>m</code> with it, then calls
|
|
<code>try_lock()</code></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>L lk(m,b);</code></entry>
|
|
<entry>Constructs an object <code>lk</code>, and associates mutex
|
|
object <code>m</code> with it, then if <code>b</code>, calls
|
|
<code>lock()</code></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>lk.try_lock()</code></entry>
|
|
<entry>If locked(), throws <code>boost::lock_error</code>. Makes a
|
|
non-blocking attempt to lock the associated mutex object,
|
|
returning <code>true</code> if the lock attempt is successful,
|
|
otherwise <code>false</code>. If the associated mutex object is
|
|
already locked by the same thread the behavior is dependent on the
|
|
<link linkend="threads.concepts.locking-strategies">locking
|
|
strategy</link> of the associated mutex object.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table></para>
|
|
</section>
|
|
<section id="threads.concepts.ScopedTimedLock">
|
|
<title>ScopedTimedLock Concept</title>
|
|
<para>A ScopedTimedLock is a refinement of <link
|
|
linkend="threads.concepts.Lock">Lock</link>. For a ScopedTimedLock type
|
|
<code>L</code> and an object <code>lk</code> of that type, and an object
|
|
<code>m</code> of a type meeting the <link
|
|
linkend="threads.concepts.TimedMutex">TimedMutex</link> requirements, and
|
|
an object <code>b</code> of type <code>bool</code>, and an object
|
|
<code>t</code> of type <classname>boost::xtime</classname>, the following
|
|
expressions must be well-formed and have the indicated effects.
|
|
<table><title>ScopedTimedLock Concept</title>
|
|
<tgroup cols="2">
|
|
<thead>
|
|
<row>
|
|
<entry>Expression</entry>
|
|
<entry>Effects</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry><code>L lk(m,t);</code></entry>
|
|
<entry>Constructs an object <code>lk</code>, and associates mutex
|
|
object <code>m</code> with it, then calls
|
|
<code>timed_lock(t)</code></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>L lk(m,b);</code></entry>
|
|
<entry>Constructs an object <code>lk</code>, and associates mutex
|
|
object <code>m</code> with it, then if <code>b</code>, calls
|
|
<code>lock()</code></entry>
|
|
</row>
|
|
<row>
|
|
<entry><code>lk.timed_lock(t)</code></entry>
|
|
<entry>If locked(), throws
|
|
<classname>boost::lock_error</classname>. Makes a blocking attempt
|
|
to lock the associated mutex object, and returns <code>true</code>
|
|
if successful within the specified time <code>t</code>, otherwise
|
|
<code>false</code>. If the associated mutex object is already
|
|
locked by the same thread the behavior is dependent on the <link
|
|
linkend="threads.concepts.locking-strategies">locking
|
|
strategy</link> of the associated mutex object.</entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table></para>
|
|
</section>
|
|
</section>
|
|
</section>
|