<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>Shared Locking Revision 2</title>
    <meta name="description" content="read write mutexes, read write locks"/>
    <style type="text/css">
    p {text-align:justify}
    li {text-align:justify}
    blockquote.note
    {
        background-color:#E0E0E0;
        padding-left: 15px;
        padding-right: 15px;
        padding-top: 1px;
        padding-bottom: 1px;
    }
    ins {background-color:#A0FFA0}
    del {background-color:#FFA0A0}
    </style>
</head>
<body>

<address align=right>
Document number: N3659<br>
Revision/subset of: N3568<br>
<br>
<a href="mailto:howard.hinnant@gmail.com">Howard Hinnant</a><br>
<a href="mailto:dv@vollmann.ch">Detlef Vollmann</a><br>
<a href="mailto:hans.boehm@hp.com">Hans Boehm</a><br>
2013-04-19
</address>
<hr>
<h1 align=center>Shared locking in C++</h1>
<h2 align=center>Revision 2</h2>

<h2>Status</h2>

<p>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3568.html">N3568</a>
was presented at the Spring 2013 meeting in Bristol to SG1
(Concurrency and Parallelism).
The decision was to bring <code>shared_mutex</code> only forward for C++14.
Also the specification should allow for spurious failures.
</p>

<h2>Changes compared to <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3568.html">N3568</a></h2>

<p>
This paper only includes the proposed wording.
For background please refer to <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3568.html">N3568</a>.
<code>upgrade_mutex</code> and <code>upgrade_lock</code> was removed.
Formal wording was added to allow for spurious failures.
Editorial note: Defining <i>shared mutex types</i> (plural) while we are only
providing one singe type (<tt>shared_mutex</tt> may sound strange.
However, the text is written as requirements, and (implementations or) programmers
may provide additional types that fulfill these requirements.
For the other mutex types we don't have the problem as we always have more than one
type for a specific requirement set.
</p>


<h2><a name="Wording"></a>Proposed Wording</h2>

<p>
Add a synopsis to 30.4. [thread.mutex] as follows:
</p>

<blockquote>
<h4>Header <tt>&lt;shared_mutex&gt;</tt> synopsis</h4>
<pre>
namespace std {

class shared_mutex;
template &lt;class Mutex&gt; class shared_lock;
template &lt;class Mutex&gt;
  void swap(shared_lock&lt;Mutex&gt;&amp; x, shared_lock&lt;Mutex&gt;&amp; y) noexcept;

}  // std
</pre>
</blockquote>

<p>
Modify 30.4.1.2 [thread.mutex.requirements.mutex] as follows:
</p>

<blockquote>
<p>
1 The <i>mutex types</i> are the standard library types <tt>std::mutex</tt>,
<tt>std::recursive_mutex</tt>, <tt>std::timed_mutex</tt>, <del>and</del>
<tt>std::recursive_timed_mutex</tt><ins> and <tt>std::shared_mutex</tt></ins>.
They shall meet the requirements set out in
this section. In this description, <tt>m</tt> denotes an object of a mutex type.
</p>
</blockquote>

<p>
Modify 30.4.1.3 [thread.timedmutex.requirements] as follows:
</p>

<blockquote>
1 The <i>timed mutex</i> types are the standard library types
<tt>std::timed_mutex</tt><ins>,</ins> <del>and</del>
<tt>std::recursive_timed_mutex</tt><ins> and <tt>std::shared_mutex</tt></ins>.
They shall meet the requirements set out
below. In this description, <tt>m</tt> denotes an object of a mutex type,
<tt>rel_time</tt> denotes an object of an instantiation of <tt>duration</tt>
(20.11.5), and <tt>abs_time</tt> denotes an object of an instantiation of
<tt>time_point</tt> (20.11.6).
</blockquote>

<p>
Insert a new section: 30.4.1.4 Shared mutex types [thread.sharedmutex.requirements]
</p>

<blockquote>
<p>
The standard library type <tt>std::shared_mutex</tt> is a <i>shared mutex type</i>.
Shared mutex types shall meet the
requirements of timed mutex types ([thread.timedmutex.requirements]), and
additionally shall meet the requirements set out below.  In this description,
<tt>m</tt> denotes an object of a mutex type, <tt>rel_type</tt> denotes an
object of an instantiation of <tt>duration</tt> (20.11.5), and <tt>abs_time</tt>
denotes an object of an instantiation of <tt>time_point</tt> (20.11.6).
</p>

<p>
In addition to the exclusive lock ownership mode specified in
[thread.mutex.requirements.mutex], shared mutex types provide a <i>shared lock</i>
ownership mode.  Multiple execution agents can simultaneously hold a
shared lock ownership of a shared mutex type.  But no execution agent shall hold
a shared lock while another execution agent holds an exclusive lock on the same
shared mutex type, and vice-versa.  The maximum number of execution agents which
can share a shared lock on a single shared mutex type is unspecified, but shall
be at least 10000.  If more than the maximum number of execution agents attempt
to obtain a shared lock, the excess execution agents shall block until the
number of shared locks are reduced below the maximum amount by other execution
agents releasing their shared lock.
</p>

<p>
The expression <tt>m.lock_shared()</tt> shall be well-formed and have the
following semantics:
</p>

<blockquote>
<p>
<i>Requires:</i> The calling thread has no ownership of the mutex.
</p>
<p>
<i>Effects:</i> Blocks the calling thread until shared ownership of the mutex
can be obtained for the calling thread.
</p>
<p>
<i>Postcondition:</i> The calling thread has a shared lock on the mutex.
</p>
<p>
<i>Return type:</i> <tt>void</tt>.
</p>
<p>
<i>Synchronization:</i> Prior <tt>unlock()</tt> operations on the same
object shall synchronize with (1.10) this operation.
</p>
<p>
<i>Throws:</i> system_error when an exception is required (30.2.2).
</p>
<p>
<i>Error conditions:</i>
</p>
<ul>
<li><tt>operation_not_permitted</tt> &mdash; if the thread does not have the
privilege to perform the operation.</li>
<li><tt>resource_deadlock_would_occur</tt> &mdash; if the implementation detects
that a deadlock would occur.</li>
<li><tt>device_or_resource_busy</tt> &mdash;  if the mutex is already locked and
blocking is not possible.</li>
</ul>
</blockquote>

<p>
The expression <tt>m.unlock_shared()</tt> shall be well-formed and have the
following semantics:
</p>

<blockquote>
<p>
<i>Requires:</i> The calling thread shall hold a shared lock on the mutex.
</p>
<p>
<i>Effects:</i> Releases a shared lock on the mutex held by the calling thread.
</p>
<p>
<i>Return type:</i> <tt>void</tt>.
</p>
<p>
<i>Synchronization:</i> This operation <i>synchronizes with</i> (1.10)
subsequent <code>lock()</code> operations that obtain ownership on the same object.
</p>
<p>
<i>Throws:</i> Nothing.
</p>
</blockquote>

<p>
The expression <tt>m.try_lock_shared()</tt> shall be well-formed and have the
following semantics:
</p>

<blockquote>
<p>
<i>Requires:</i> The calling thread has no ownership of the mutex.
</p>
<p>
<i>Effects:</i> Attempts to obtain shared ownership of the mutex for the calling
thread without blocking. If shared ownership is not obtained, there is no effect
and <tt>try_lock_shared()</tt> immediately returns.
An implementation may fail to obtain the lock even if it is not held by any other thread.

</p>
<p>
<i>Return type:</i> <code>bool</code>.
</p>
<p>
<i>Returns:</i> <code>true</code> if the shared ownership lock was acquired,
<code>false</code> otherwise.
</p>
<p>
<i>Synchronization:</i> If <tt>try_lock_shared()</tt> returns <tt>true</tt>,
prior <tt>unlock()</tt> operations on the same object <i>synchronize
with</i> (1.10) this operation.
</p>
<p>
<i>Throws:</i> Nothing.
</p>
</blockquote>

<p>
The expression <tt>m.try_lock_shared_for(rel_time)</tt> shall be well-formed and
have the following semantics:
</p>

<blockquote>
<p>
<i>Requires:</i> The calling thread has no ownership of the mutex.
</p>
<p>
<i>Effects:</i> If the tick <tt>period</tt> of <tt>rel_time</tt> is not exactly
convertible to the native tick <tt>period</tt>, the <tt>duration</tt> shall be
rounded up to the nearest native tick <tt>period</tt>. Attempts to obtain shared
lock ownership for the calling thread within the relative timeout (30.2.4)
specified by <code>rel_time</code>. If the time specified by <tt>rel_time</tt>
is less than or equal to <tt>rel_time.zero()</tt>, the function attempts to
obtain ownership without blocking (as if by calling
<tt>try_lock_shared()></tt>). The function shall return within the timeout
specified by <tt>rel_time</tt> only if it has obtained shared ownership of the
mutex object.
[ Note: As with <tt>try_lock()</tt>, there is no guarantee that ownership will be
obtained if the lock is available, but implementations are expected to make a strong
effort to do so. --end note ]

</p>
<p>
<i>Return type:</i> <code>bool</code>.
</p>
<p>
<i>Returns:</i> <code>true</code> if the shared lock was acquired,
<code>false</code> otherwise.
</p>
<p>
<i>Synchronization:</i> If <tt>try_lock_shared_for()</tt> returns <tt>true</tt>,
prior <tt>unlock()</tt> operations on the same object <i>synchronize
with</i> (1.10) this operation.
</p>
<p>
<i>Throws:</i> Nothing.
</p>
</blockquote>

<p>
The expression <tt>m.try_lock_shared_until(abs_time)</tt> shall be well-formed
and have the following semantics:
</p>

<blockquote>
<p>
<i>Requires:</i> The calling thread has no ownership of the mutex.
</p>
<p>
<i>Effects:</i> The function attempts to obtain shared ownership of the mutex.
If <tt>abs_time</tt> has already passed, the function attempts to obtain shared
ownership without blocking (as if by calling <tt>try_lock_shared()</tt>). The
function shall return before the absolute timeout (30.2.4) specified by
<tt>abs_time</tt> only if it has obtained shared ownership of the mutex object.
[ Note: As with <tt>try_lock()</tt>, there is no guarantee that ownership will be
obtained if the lock is available, but implementations are expected to make a strong
effort to do so. --end note ]
</p>
<p>
<i>Return type:</i> <code>bool</code>.
</p>
<p>
<i>Returns:</i> <code>true</code> if the shared lock was acquired,
<code>false</code> otherwise.
</p>
<p>
<i>Synchronization:</i> If <tt>try_lock_shared_until()</tt> returns
<tt>true</tt>, prior <tt>unlock()</tt> operations on the same object
<i>synchronize with</i> (1.10) this operation.
</p>
<p>
<i>Throws:</i> Nothing.
</p>
</blockquote>

</blockquote>

<p>
Insert a new section: 30.4.1.4.1 Class <tt>shared_mutex</tt> [thread.sharedmutex.class]
</p>

<blockquote>
<pre>
namespace std {

class shared_mutex
{
public:

    shared_mutex();
    ~shared_mutex();

    shared_mutex(const shared_mutex&amp;) = delete;
    shared_mutex&amp; operator=(const shared_mutex&amp;) = delete;

    // Exclusive ownership

    void lock();  // blocking
    bool try_lock();
    template &lt;class Rep, class Period&gt;
        bool try_lock_for(const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
    template &lt;class Clock, class Duration&gt;
        bool
        try_lock_until(const chrono::time_point&lt;Clock, Duration&gt;&amp; abs_time);
    void unlock();

    // Shared ownership

    void lock_shared();  // blocking
    bool try_lock_shared();
    template &lt;class Rep, class Period&gt;
        bool
        try_lock_shared_for(const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
    template &lt;class Clock, class Duration&gt;
        bool
        try_lock_shared_until(
                      const chrono::time_point&lt;Clock, Duration&gt;&amp; abs_time);
    void unlock_shared();
};

}  // std
</pre>

<p>
The class <tt>shared_mutex</tt> provides a non-recursive mutex with shared
ownership semantics.
</p>

<p>
The class <tt>shared_mutex</tt> shall satisfy all of the <tt>SharedMutex</tt>
requirements ([thread.sharedmutex.requirements]). It shall be a standard-layout
class (Clause 9).
</p>

<p>
The behavior of a program is undefined if:
</p>

<ul>
<li>it destroys a <tt>shared_mutex</tt> object owned by any thread,</li>
<li>a thread attempts to recursively gain any ownership of a
<tt>shared_mutex</tt>.</li>
<li>a thread terminates while possessing any ownership of a
<tt>shared_mutex</tt>.</li>
</ul>

</blockquote>


<p>
Add a new section 30.4.2.3 Class template <tt>shared_lock</tt> [thread.lock.shared]:
</p>

<blockquote>

<h4>Class template <code>shared_lock</code> [thread.lock.shared]</h4>

<blockquote><pre>
namespace std {

template &lt;class Mutex&gt;
class shared_lock
{
public:
    typedef Mutex mutex_type;

    // Shared locking

    shared_lock() noexcept;
    explicit shared_lock(mutex_type&amp; m);  // blocking
    shared_lock(mutex_type&amp; m, defer_lock_t) noexcept;
    shared_lock(mutex_type&amp; m, try_to_lock_t);
    shared_lock(mutex_type&amp; m, adopt_lock_t);
    template &lt;class Clock, class Duration&gt;
        shared_lock(mutex_type&amp; m,
                    const chrono::time_point&lt;Clock, Duration&gt;&amp; abs_time);
    template &lt;class Rep, class Period&gt;
        shared_lock(mutex_type&amp; m,
                    const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
    ~shared_lock();

    shared_lock(shared_lock const&amp;) = delete;
    shared_lock&amp; operator=(shared_lock const&amp;) = delete;

    shared_lock(shared_lock&amp;&amp; u) noexcept;
    shared_lock&amp; operator=(shared_lock&amp;&amp; u) noexcept;

    void lock();  // blocking
    bool try_lock();
    template &lt;class Rep, class Period&gt;
        bool try_lock_for(const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
    template &lt;class Clock, class Duration&gt;
        bool
        try_lock_until(const chrono::time_point&lt;Clock, Duration&gt;&amp; abs_time);
    void unlock();

    // Setters

    void swap(shared_lock&amp; u) noexcept;
    mutex_type* release() noexcept;

    // Getters

    bool owns_lock() const noexcept;
    explicit operator bool () const noexcept;
    mutex_type* mutex() const noexcept;

private:
    mutex_type* pm; <i>// exposition only</i>
    bool owns;      <i>// exposition only</i>
};

template &lt;class Mutex&gt;
  void swap(shared_lock&lt;Mutex&gt;&amp; x, shared_lock&lt;Mutex&gt;&amp; y) noexcept;

}  // std
</pre></blockquote>

<p>
An object of type <tt>shared_lock</tt> controls the shared ownership of a lockable object within
a scope. Shared ownership of the lockable object may be acquired at construction or
after construction, and may be transferred, after acquisition, to another
<tt>shared_lock</tt> object. Objects of type <tt>shared_lock</tt> are not copyable but are
movable. The behavior of a program is undefined if the contained pointer <tt>pm</tt> is
not null and the lockable object pointed to by <tt>pm</tt> does not exist for the entire
remaining lifetime (3.8) of the <tt>shared_lock</tt> object. The supplied <tt>Mutex</tt> type
shall meet the shared mutex requirements ([thread.sharedmutex.requirements]).
</p>

<p>
[<i>Note:</i> <tt>shared_lock&lt;Mutex&gt;</tt> meets the TimedLockable
requirements (30.2.5.4). &mdash; <i>end note</i>]
</p>

<h5><tt>shared_lock</tt> constructors, destructor, and assignment [thread.lock.shared.cons]</h5>

<pre>
shared_lock() noexcept;
</pre>

<blockquote>
<p>
<i>Effects:</i> Constructs an object of type <tt>shared_lock</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == nullptr</tt> and <tt>owns == false</tt>.
</p>
</blockquote>

<pre>
explicit shared_lock(mutex_type&amp; m);
</pre>

<blockquote>
<p>
<i>Requires:</i> The calling thread does not own the mutex for any ownership
mode.
</p>
<p>
<i>Effects:</i> Constructs an object of type <tt>shared_lock</tt> and calls
<tt>m.lock_shared()</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == &amp;m</tt> and <tt>owns == true</tt>.
</p>
</blockquote>

<pre>
shared_lock(mutex_type&amp; m, defer_lock_t) noexcept;
</pre>

<blockquote>
<p>
<i>Effects:</i> Constructs an object of type <tt>shared_lock</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == &amp;m</tt> and <tt>owns == false</tt>.
</p>
</blockquote>

<pre>
shared_lock(mutex_type&amp; m, try_to_lock_t);
</pre>

<blockquote>
<p>
<i>Requires:</i> The calling thread does not own the mutex for any ownership
mode.
</p>
<p>
<i>Effects:</i> Constructs an object of type <tt>shared_lock</tt> and calls
<tt>m.try_lock_shared()</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == &amp;m</tt> and <tt>owns == res</tt> where
<tt>res</tt> is the value returned by the call to <tt>m.try_lock_shared()</tt>.
</p>
</blockquote>

<pre>
shared_lock(mutex_type&amp; m, adopt_lock_t);
</pre>

<blockquote>
<p>
<i>Requires:</i> The calling thread has shared ownership of the mutex.
</p>
<p>
<i>Effects:</i> Constructs an object of type <tt>shared_lock</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == &amp;m</tt> and <tt>owns == true</tt>.
</p>
</blockquote>

<pre>
template &lt;class Clock, class Duration&gt;
    shared_lock(mutex_type&amp; m,
                const chrono::time_point&lt;Clock, Duration&gt;&amp; abs_time);
</pre>

<blockquote>
<p>
<i>Requires:</i> The calling thread does not own the mutex for any ownership
mode.
</p>
<p>
<i>Effects:</i> Constructs an object of type <tt>shared_lock</tt> and calls
<tt>m.try_lock_shared_until(abs_time)</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == &amp;m</tt> and <tt>owns == res</tt> where
<tt>res</tt> is the value returned by the call to
<tt>m.try_lock_shared_until(abs_time)</tt>.
</p>
</blockquote>

<pre>
template &lt;class Rep, class Period&gt;
    shared_lock(mutex_type&amp; m,
                const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
</pre>

<blockquote>
<p>
<i>Requires:</i> The calling thread does not own the mutex for any ownership
mode.
</p>
<p>
<i>Effects:</i> Constructs an object of type <tt>shared_lock</tt> and calls
<tt>m.try_lock_shared_for(rel_time)</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == &amp;m</tt> and <tt>owns == res</tt> where
<tt>res</tt> is the value returned by the call to
<tt>m.try_lock_shared_for(rel_time)</tt>.
</p>
</blockquote>

<pre>
~shared_lock();
</pre>

<blockquote>
<p>
<i>Effects:</i> If <tt>owns</tt> calls <tt>pm-&gt;unlock_shared()</tt>.
</p>
</blockquote>

<pre>
shared_lock(shared_lock&amp;&amp; sl) noexcept;
</pre>

<blockquote>
<p>
<i>Postconditions:</i> <tt>pm == &amp;sl_p.pm</tt> and <tt>owns == sl_p.owns</tt>
(where <tt>sl_p</tt> is the state of <tt>sl</tt> just prior to this construction),
<tt>sl.pm == nullptr</tt> and <tt>sl.owns ==  false</tt>.
</p>
</blockquote>

<pre>
shared_lock&amp; operator=(shared_lock&amp;&amp; sl) noexcept;
</pre>

<blockquote>
<p>
<i>Effects:</i> If <tt>owns</tt> calls <tt>pm-&gt;unlock_shared()</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == &amp;sl_p.pm</tt> and <tt>owns == sl_p.owns</tt>
(where <tt>sl_p</tt> is the state of <tt>sl</tt> just prior to this assignment),
<tt>sl.pm == nullptr</tt> and <tt>sl.owns ==  false</tt>.
</p>
</blockquote>

<h5><tt>shared_lock</tt> locking [thread.lock.shared.locking]</h5>

<pre>
void lock();
</pre>

<blockquote>
<p>
<i>Effects:</i> <tt>pm-&gt;lock_shared()</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>owns == true</tt>.
</p>
<p>
<i>Throws:</i> Any exception thrown by <tt>pm-&gt;lock_shared()</tt>.
<tt>system_error</tt> if an exception is required (30.2.2).
<tt>system_error</tt> with an error condition of
<tt>operation_not_permitted</tt> if <tt>pm</tt> is <tt>nullptr</tt>.
<tt>system_error</tt> with an error condition of
<tt>resource_deadlock_would_occur</tt> if on entry <tt>owns</tt> is
<tt>true</tt>.
</p>
</blockquote>

<pre>
bool try_lock();
</pre>

<blockquote>
<p>
<i>Effects:</i> <tt>pm-&gt;try_lock_shared()</tt>.
</p>
<p>
<i>Returns:</i> The value returned by the call to
<tt>pm-&gt;try_lock_shared()</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>owns == res</tt>, where <tt>res</tt> is the value
returned by the call to <tt>pm-&gt;try_lock_shared()</tt>.
</p>
<p>
<i>Throws:</i> Any exception thrown by <tt>pm-&gt;try_lock_shared()</tt>.
<tt>system_error</tt> if an exception is required (30.2.2).
<tt>system_error</tt> with an error condition of
<tt>operation_not_permitted</tt> if <tt>pm</tt> is <tt>nullptr</tt>.
<tt>system_error</tt> with an error condition of
<tt>resource_deadlock_would_occur</tt> if on entry <tt>owns</tt> is
<tt>true</tt>.
</p>
</blockquote>

<pre>
template &lt;class Clock, class Duration&gt;
    bool
    try_lock_until(const chrono::time_point&lt;Clock, Duration&gt;&amp; abs_time);
</pre>

<blockquote>
<p>
<i>Effects:</i> <tt>pm-&gt;try_lock_shared_until(abs_time)</tt>.
</p>
<p>
<i>Returns:</i> The value returned by the call to
<tt>pm-&gt;try_lock_shared_until(abs_time)</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>owns == res</tt>, where <tt>res</tt> is the value
returned by the call to <tt>pm-&gt;try_lock_shared_until(abs_time)</tt>.
</p>
<p>
<i>Throws:</i> Any exception thrown by
<tt>pm-&gt;try_lock_shared_until(abs_time)</tt>. <tt>system_error</tt> if an
exception is required (30.2.2). <tt>system_error</tt> with an error condition of
<tt>operation_not_permitted</tt> if <tt>pm</tt> is <tt>nullptr</tt>.
<tt>system_error</tt> with an error condition of
<tt>resource_deadlock_would_occur</tt> if on entry <tt>owns</tt> is
<tt>true</tt>.
</p>
</blockquote>

<pre>
template &lt;class Rep, class Period&gt;
    bool try_lock_for(const chrono::duration&lt;Rep, Period&gt;&amp; rel_time);
</pre>

<blockquote>
<p>
<i>Effects:</i> <tt>pm-&gt;try_lock_shared_for(rel_time)</tt>.
</p>
<p>
<i>Returns:</i> The value returned by the call to
<tt>pm-&gt;try_lock_shared_for(rel_time)</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>owns == res</tt>, where <tt>res</tt> is the value
returned by the call to <tt>pm-&gt;try_lock_shared_for(rel_time)</tt>.
</p>
<p>
<i>Throws:</i> Any exception thrown by
<tt>pm-&gt;try_lock_shared_for(rel_time)</tt>. <tt>system_error</tt> if an
exception is required (30.2.2). <tt>system_error</tt> with an error condition of
<tt>operation_not_permitted</tt> if <tt>pm</tt> is <tt>nullptr</tt>.
<tt>system_error</tt> with an error condition of
<tt>resource_deadlock_would_occur</tt> if on entry <tt>owns</tt> is
<tt>true</tt>.
</p>
</blockquote>

<pre>
void unlock();
</pre>

<blockquote>
<p>
<i>Effects:</i> <tt>pm-&gt;unlock_shared()</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>owns == false</tt>.
</p>
<p>
<i>Throws:</i> <tt>system_error</tt> when an exception is required (30.2.2).
</p>
<p>
<i>Error conditions:</i>
</p>
<ul>
<li><tt>operation_not_permitted</tt> &mdash; if on entry <tt>owns</tt> is
<tt>false</tt>.</li>
</ul>
</blockquote>

<h5><tt>shared_lock</tt> modifiers [thread.lock.shared.mod]</h5>

<pre>
void swap(shared_lock&amp; sl) noexcept;
</pre>

<blockquote>
<p>
<i>Effects:</i> Swaps the data members of <tt>*this</tt> and <tt>sl</tt>.
</p>
</blockquote>

<pre>
mutex_type* release() noexcept;
</pre>

<blockquote>
<p>
<i>Returns:</i> The previous value of <tt>pm</tt>.
</p>
<p>
<i>Postconditions:</i> <tt>pm == nullptr</tt> and <tt>owns == false</tt>.
</p>
</blockquote>

<pre>
template &lt;class Mutex&gt;
  void swap(shared_lock&lt;Mutex&gt;&amp; x, shared_lock&lt;Mutex&gt;&amp; y) noexcept;
</pre>

<blockquote>
<p>
<i>Effects:</i> <tt>x.swap(y)</tt>.
</p>
</blockquote>

<h5><tt>shared_lock</tt> observers [thread.lock.shared.obs]</h5>

<pre>
bool owns_lock() const noexcept;
</pre>

<blockquote>
<p>
<i>Returns:</i> <tt>owns</tt>.
</p>
</blockquote>

<pre>
explicit operator bool () const noexcept;
</pre>

<blockquote>
<p>
<i>Returns:</i> <tt>owns</tt>.
</p>
</blockquote>

<pre>
mutex_type* mutex() const noexcept;
</pre>

<blockquote>
<p>
<i>Returns:</i> <tt>pm</tt>.
</p>
</blockquote>

</blockquote>
</body>
</html>
