This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++14 status.

### 2288. Inconsistent requirements for shared mutexes

Section: 32.5.4.4 [thread.sharedmutex.requirements] Status: C++14 Submitter: Daniel Krügler Opened: 2013-08-30 Last modified: 2016-01-28

Priority: Not Prioritized

View all issues with C++14 status.

Discussion:

Albeit shared mutex types refine timed mutex types, the requirements imposed on the corresponding required member function expressions are inconsistent in several aspects, most probably because failing synchronisation with wording changes for timed mutexes applied by some issues:

1. Due to acceptance of N3568 a wording phrase came in 32.5.4.4 [thread.sharedmutex.requirements] p26,

   > Effects: 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. […]

   while a very similar one had been removed for 32.5.4.3 [thread.timedmutex.requirements] by LWG 2091.

   Having this guaranteed effect for `try_lock_shared_for` but not for `try_lock_for` seems inconsistent and astonishing.

   If the actual intended restriction imposed onto the implementation is to forbid early wakeups here, we should ensure that to hold for timed mutex's `try_lock_for` as well. Note that the rationale provided for LWG 2091 was a potential late wakeup situation, but it seems that there is no implementation restriction that prevents early wakeups.

2. The shared-lock requirements for any `*lock*()` functions don't provide the guarantee that "If an exception is thrown then a lock shall not have been acquired for the current execution agent.". For other mutex types this guarantee can be derived from the corresponding `TimedLockable` requirements, but there are no `SharedLockable` requirements.

3. The shared-lock requirements for `*lock_for/_until()` functions require "Throws: Nothing." instead of "Throws: Timeout-related exceptions (30.2.4)." which had been added by LWG 2093, because user-provided clocks, durations, or time points may throw exceptions.

4. With the addition of `std::shared_mutex`, the explicit lists of 32.5.4.2 [thread.mutex.requirements.mutex] p7+15,

   > Requires: If `m` is of type `std::mutex` or `std::timed_mutex`, the calling thread does not own the mutex.

   and of 32.5.4.3 [thread.timedmutex.requirements] p4+11,

   > Requires: If `m` is of type `std::timed_mutex`, the calling thread does not own the mutex.

   are incomplete and should add the non-recursive `std::shared_mutex` as well.

[2014-02-16: Moved as Immediate]

Proposed resolution:

This wording is relative to N3691.

1. Change 32.5.4.2 [thread.mutex.requirements.mutex] p7+15 as indicated:

   > -6- The expression `m.lock()` shall be well-formed and have the following semantics:

   > > -7- Requires: If `m` is of type `std::mutex` or, `std::timed_mutex`, or `std::shared_mutex`, the calling thread does not own the mutex.

   > […]

   > -14- The expression `m.try_lock()` shall be well-formed and have the following semantics:

   > > -15- Requires: If `m` is of type `std::mutex` or, `std::timed_mutex`, or `std::shared_mutex`, the calling thread does not own the mutex.

2. Change 32.5.4.3 [thread.timedmutex.requirements] p4+11 as indicated:

   > -3- The expression `m.try_lock_for(rel_time)` shall be well-formed and have the following semantics:

   > > -4- Requires: If `m` is of type `std::timed_mutex` or `std::shared_mutex`, the calling thread does not own the mutex.

   > […]

   > -10- The expression `m.try_lock_until(abs_time)` shall be well-formed and have the following semantics:

   > > -11- Requires: If `m` is of type `std::timed_mutex` or `std::shared_mutex`, the calling thread does not own the mutex.

3. Change 32.5.4.4 [thread.sharedmutex.requirements] as indicated:

   > -3- The expression `m.lock_shared()` shall be well-formed and have the following semantics:

   > > -4- Requires: The calling thread has no ownership of the mutex.

   > > -5- Effects: Blocks the calling thread until shared ownership of the mutex can be obtained for the calling thread. If an exception is thrown then a shared lock shall not have been acquired for the current thread.

   > > […]

   > -24- The expression `m.try_lock_shared_for(rel_time)` shall be well-formed and have the following semantics:

   > > -25- Requires: The calling thread has no ownership of the mutex.

   > > -26- Effects: 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 shared lock ownership for the calling thread within the relative timeout (30.2.4) 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_shared()`). The function shall return within the timeout specified by `rel_time` only if it has obtained shared ownership of the mutex object. [Note: As with `try_lock()`, 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] If an exception is thrown then a shared lock shall not have been acquired for the current thread.

   > > […]

   > > -30- Throws: Nothing Timeout-related exceptions (32.2.4 [thread.req.timing]).

   > -31- The expression `m.try_lock_shared_until(abs_time)` shall be well-formed and have the following semantics:

   > > -32- Requires: The calling thread has no ownership of the mutex.

   > > -33- Effects: The function attempts to obtain shared ownership of the mutex. If `abs_time` has already passed, the function attempts to obtain shared ownership without blocking (as if by calling `try_lock_shared()`). The function shall return before the absolute timeout (30.2.4) specified by `abs_time` only if it has obtained shared ownership of the mutex object. [Note: As with `try_lock()`, 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] If an exception is thrown then a shared lock shall not have been acquired for the current thread.

   > > […]

   > > -37- Throws: Nothing Timeout-related exceptions (32.2.4 [thread.req.timing]).