Timed lock algorithms for multiple lockables

Revision History

R3

Modifications after 2026-06_Brno:SG18 meeting:

R2

Modifications after P3832R1:SG1:Brno_2026 meeting:

R1

Modifications after 2025-11_Kona:SG1-P3832R0 meeting:

Abstract

C++11 introduced std::lock and std::try_lock (and C++17 introduced std::scoped_lock) to simplify deadlock-free acquisition of multiple lockables. These algorithms support BasicLockable and Lockable objects, but there is currently no facility for timed acquisition of multiple TimedLockable objects.

Users who require timeout-based locking of multiple mutexes must implement their own deadlock-avoidance algorithm, typically via try_lock(), unlock(), and retry. This is error-prone, verbose, and inconsistent with the existing standard library facilities.

This paper proposes two new algorithms:

template <class Clock, class Duration, class... Ls>
int try_lock_until(const std::chrono::time_point<Clock, Duration>& tp, Ls&... ls);

template <class Rep, class Period, class... Ls>
int try_lock_for(const std::chrono::duration<Rep, Period>& rel_time, Ls&... ls);

These extend the std::lock family of functions to timed lockables, enabling consistent and safe use of multiple timed mutexes.

Contents

  1. Revision History
  2. Abstract
  3. Impact on the Standard
  4. Design Rationale
  5. Proposed Wording
  6. Example
  7. Implementation Experience
  8. Acknowledgments
  9. References

3. Impact on the Standard

4. Design Rationale

5. Proposed Wording

The following changes are relative to N5046.

17.3.2 Header <version> synopsis [version.syn]

Add to [version.syn]:

#define __cpp_lib_timed_lock_alg  20XXXXL // also in <mutex>

32.6.2 Header <mutex> synopsis [thread.mutex.syn]

Add to [thread.mutex.syn], after the declarations of lock and try_lock:

template <class Clock, class Duration, class... Ls>
  int try_lock_until(const chrono::time_point<Clock, Duration>& abs_time, Ls&... ls);
template <class Rep, class Period, class... Ls>
  int try_lock_for(const chrono::duration<Rep, Period>& rel_time, Ls&... ls);

32.6.6 Generic locking algorithms [thread.lock.algorithm]

In 32.6.6, after point 5, add:

template <class Clock, class Duration, class... Ls>
int try_lock_until(const chrono::time_point<Clock, Duration>& abs_time, Ls&... ls);
6
Preconditions:
Each template parameter type meets the Cpp17TimedLockable requirements.
7
Effects:
Calls try_lock_until(l, abs_time) on an unspecified lockable l from ls. If this returns true, then try_lock() is called on the other lockables of ls in an unspecified order until all have been locked or one fails. If all calls to try_lock() return true, the overall operation succeeds. Otherwise, unlock() is called for all lockables for which try_lock() or try_lock_until() returned true, and the above steps are repeated until the time point abs_time has been reached. The sequence in which locks are obtained does not result in deadlock. This function does not rely on timeouts for deadlock avoidance. This function does not consistently return ≥ 0 in the absence of contending mutex acquisitions. If a call to try_lock_until() or try_lock() throws an exception, unlock() is called for any lockable that was locked by this algorithm prior to the exception, and the exception is rethrown.
[Note 1: The lockable l chosen may be the same or different for multiple executions of the loop. — end note]
8
Returns:
-1 if all locks were obtained, otherwise the index of the lockable that the implementation was attempting to acquire when the time point abs_time was reached.
9
Throws:
Any exception thrown by the lockable try_lock_until() or try_lock() functions.
template <class Rep, class Period, class... Ls>
int try_lock_for(const chrono::duration<Rep, Period>& rel_time, Ls&... ls);
10
Preconditions:
Each template parameter type meets the Cpp17TimedLockable requirements.
11
Effects:
Equivalent to:
return try_lock_until(chrono::steady_clock::now() + rel_time, ls...);

6. Example

std::timed_mutex m1, m2;
if (std::try_lock_for(100ms, m1, m2) == -1) {
    // success
    std::scoped_lock sl(std::adopt_lock, m1, m2);
    // ...
} else {
    // failed to acquire within timeout
}

7. Implementation Experience

Existing implementations of std::lock already use a deadlock-avoidance algorithm. Using the gcc implementation as an example, instead of locking one with m.lock() and using std::try_lock() on the rest, the algorithm could start locking one with m.try_lock_until(tp). Example at Compiler Explorer

A reference implementation is available at github.com/bemanproject/timed_lock_alg.

8. Acknowledgments

The std-proposals mailing list:

Reference implementation:

9. References