std::condit...::wait_until(3)

๐Ÿ“ฆ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

โœจ std::condition_variable_any::wait_until โœจ

bestie, you're in the C++ Standard Library docs, and this page is all about std::condition_variable_any::wait_until โ€” the "wait until a specific time point" flavor of waiting. it's giving patient queen energy ๐Ÿ‘‘


๐ŸŒธ SYNOPSIS ๐ŸŒธ

template< class Lock, class Clock, class Duration >
std::cv_status
    wait_until( Lock& lock,                                      (1) (since C++11)
                const std::chrono::time_point<Clock, Duration>& abs_time );

template< class Lock, class Clock, class Duration, class Predicate >
bool wait_until( Lock& lock,                                     (2) (since C++11)
                 const std::chrono::time_point<Clock, Duration>& abs_time,
                 Predicate pred );

template< class Lock, class Clock, class Duration, class Predicate >
bool wait_until( Lock& lock, std::stop_token stoken,             (3) (since C++20)
                 const std::chrono::time_point<Clock, Duration>& abs_time,
                 Predicate pred );

okay so here's the whole vibe: wait_until makes the current thread block until one of these three things happens โ€” the condition variable gets notified, the given duration has been elapsed, or a spurious wakeup occurs ๐Ÿ˜ด and yeah, pred can be optionally provided to detect spurious wakeup. iykyk ๐Ÿซถ

1) Atomically calls lock.unlock() and blocks on *this. the thread will be unblocked when notify_all() or notify_one() is executed, or abs_time is reached. it may also be unblocked spuriously ๐Ÿ˜ญ when unblocked, it calls lock.lock() (possibly blocking on the lock), then returns.

2, 3) waiting for a specific condition to become true โ€” this is the version you use to ignore spurious awakenings โœจ

2) equivalent to:

while (!pred())
    if (wait_until(lock, abs_time) == std::cv_status::timeout)
        return pred();
return true;

3) registers *this for the duration of this call, to be notified if a stop request is made on stoken's associated stop-state; it is then equivalent to:

while (!stoken.stop_requested())
{
    if (pred())
        return true;
    if (wait_until(lock, abs_time) == std::cv_status::timeout)
        return pred();
}
return pred();
โš ๏ธ right after wait_until returns, lock is locked by the calling thread. if this postcondition cannot be satisfied^[1], it calls std::terminate ๐Ÿ’€ ^[1] this can happen if the re-locking of the mutex throws an exception.

๐ŸŽ€ PARAMETERS ๐ŸŽ€

  • lock โ€” an lock which must be locked by the calling thread ๐Ÿ”’
  • stoken โ€” a stop token to register interruption for ๐Ÿ›‘
  • abs_time โ€” the time point where waiting expires โฐ
  • pred โ€” the predicate to check whether the waiting can be completed โœ…

๐Ÿฆ‹ TYPE REQUIREMENTS ๐Ÿฆ‹

  • Lock must meet the requirements of BasicLockable. ๐Ÿงš
  • Predicate must meet the requirements of FunctionObject. ๐Ÿงš

โญ RETURN VALUE โญ

1) std::cv_status::timeout if abs_time has been reached, otherwise std::cv_status::no_timeout. โณ

2, 3) the latest result of pred() before returning to the caller. ๐Ÿ’–


๐ŸŒˆ EXCEPTIONS ๐ŸŒˆ

1) timeout-related exceptions.

2, 3) timeout-related exceptions, and any exception thrown by pred.


๐Ÿ‘‘ NOTES ๐Ÿ‘‘

the standard recommends that the clock tied to abs_time be used to measure time; that clock is not required to be a monotonic clock. there are no guarantees regarding the behavior of this function if the clock is adjusted discontinuously, but the existing implementations convert abs_time from Clock to std::chrono::system_clock and delegate to POSIX pthread_cond_timedwait so that the wait honors adjustments to the system clock, but not to the user-provided Clock. in any case, the function also may wait for longer than until after abs_time has been reached due to scheduling or resource contention delays ๐Ÿ˜ฎโ€๐Ÿ’จ

even if the clock in use is std::chrono::steady_clock or another monotonic clock, a system clock adjustment may induce a spurious wakeup ๐Ÿ™ˆ

the effects of notify_one()/notify_all() and each of the three atomic parts of wait()/wait_for()/wait_until() (unlock+wait, wakeup, and lock) take place in a single total order that can be viewed as modification order of an atomic variable: the order is specific to this individual condition variable. this makes it impossible for notify_one() to, for example, be delayed and unblock a thread that started waiting just after the call to notify_one() was made. periodt. ๐Ÿ’…


๐Ÿ“ EXAMPLE ๐Ÿ“

// Run this code

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <thread>

std::condition_variable_any cv;
std::mutex cv_m; // This mutex is used for three purposes:
                 // 1) to synchronize accesses to i
                 // 2) to synchronize accesses to std::cerr
                 // 3) for the condition variable cv
int i = 0;

void waits()
{
    std::unique_lock<std::mutex> lk(cv_m);
    std::cerr << "Waiting... \n";
    cv.wait(lk, []{ return i == 1; });
    std::cerr << "...finished waiting. i == 1\n";
}

void signals()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lk(cv_m);
        std::cerr << "Notifying...\n";
    }
    cv.notify_all();

    std::this_thread::sleep_for(std::chrono::seconds(1));

    {
        std::lock_guard<std::mutex> lk(cv_m);
        i = 1;
        std::cerr << "Notifying again...\n";
    }
    cv.notify_all();
}

int main()
{
    std::thread t1(waits), t2(waits), t3(waits), t4(signals);
    t1.join();
    t2.join();
    t3.join();
    t4.join();
}

๐Ÿ’œ Possible output:

Waiting...
Waiting...
Waiting...
Notifying...
Notifying again...
...finished waiting. i == 1
...finished waiting. i == 1
...finished waiting. i == 1

๐Ÿ”ฅ DEFECT REPORTS ๐Ÿ”ฅ

the following behavior-changing defect reports were applied retroactively to previously published C++ standards. not me being obsessed with retroactive fixes ๐Ÿ˜ญ

  • LWG 2093 โ€” applied to C++11 โ€” behavior as published: timeout-related exceptions were missing in the specification โ†’ correct behavior: mentions these exceptions โœจ
  • LWG 2135 โ€” applied to C++11 โ€” behavior as published: the behavior was unclear if lock.lock() throws an exception โ†’ correct behavior: calls std::terminate in this case ๐Ÿ’€

๐Ÿงš SEE ALSO ๐Ÿงš

  • wait โ€” blocks the current thread until the condition variable is awakened (public member function) ๐Ÿ’ค
  • wait_until โ€” blocks the current thread until the condition variable is awakened or until specified time point has been reached (public member function) โฐ

๐Ÿ™ˆ HIDDEN CATEGORY ๐Ÿ™ˆ

  • Pages with unreviewed LWG DR marker

and that's the page, bestie โ€” sign-off time ๐Ÿซถ this little gem comes to you from http://cppreference.com, version 2024.06.10 ๐ŸŽ€ slay, go wait on something responsibly ๐Ÿ’…โœจ๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from