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.6.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.6.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.6.4.3 [thread.timedmutex.requirements] by LWG 2091(i).

    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(i) 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(i), because user-provided clocks, durations, or time points may throw exceptions.

  4. With the addition of std::shared_mutex, the explicit lists of 32.6.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.6.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.6.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.6.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.6.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: NothingTimeout-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: NothingTimeout-related exceptions (32.2.4 [thread.req.timing]).