简体   繁体   中英

Condition Variable wait_until timeout_time in the past?

If I call std::condition_variable::wait_until and pass a timeout_time that is in the past, is it guaranteed to return std::cv_status::timeout ? Or is there a short window in which a notification could be delivered that results in the condition variable being unblocked the "normal" way with std::cv_status::no_timeout ?

Context: I'm implementing a timer collection manager, and intend use a wait_until on a condition variable to implement timer expiry. The simplest implementation of my desired semantics may involve passing a timeout_time in the past to std::condition_variable::wait_until and relying on it immediately returning std::cv_status::timeout (for example in case one timer expires while the processing for another timer is ongoing).

Note that my stdlib's implementation of wait_until defers to pthread_cond_timedwait , so any guarantees made by that API are applicable in this case.

TL;DR : (i) the return value of std::condition_variable::wait_until() does not appear to be guaranteed by C++ under the conditions specified, though the analogous pthreads function does guarantee that a timeout will be signaled under those conditions; (ii) even in the case where the timeout time is already past, the method's return could be delayed for an arbitrary length of time; and (iii) a single execution of the method might both consume a signal and report a timeout.

In more detail :

If I call std::condition_variable::wait_until and pass a timeout_time that is in the past, is it guaranteed to return std::cv_status::timeout ?

All versions of the spec since the introduction of <condition_variable> say substantially the same thing about the return value, for example:

Returns : cv_status::timeout if the absolute timeout (32.2.4) specified by abs_time expired, otherwise cv_status::no_timeout .

(C++20 draft 4849, paragraph 32.6.3/21)

The specs overall do not explicitly define what it means for the timeout to have expired, however. I am inclined to think that the timeout already being in the past at the time of the call should count as "the absolute timeout [...] expired", yet many other methods' specifications explicitly address behavior when the absolute timeout is already in the past at the time the method is invoked, whereas this method's do not. I cannot rule out that omission being intentional.

Since you have tagged pthreads , the design of the <condition_variable> API is strongly influenced by those specifications, and UNIX implementations of <condition_variable> typically involve thin wrappers around pthreads functions, it may be illuminating to consider the specifications for pthread_cond_timedwait() . Among them:

an error is returned if the absolute time specified by abstime passes (that is, system time equals or exceeds abstime ) before the condition cond is signaled or broadcasted, or if the absolute time specified by abstime has already been passed at the time of the call .

(Emphasis added)

Thus pthread_cond_timedwait() will definitely return an error signaling a timeout if the timeout time is already in the past when that function is called, and a wait_until() implementation that decides on that basis whether to return cv_status::timeout will behave analogously.

But also, successful completion of pthread_cond_timedwait() does not imply that the timeout has not expired by the time the function returns, and even if the function could make such a guarantee, the thread calling that function could not safely assume that the timeout would not expire between the function returning and the evaluation of a test of its return value.

Do pay careful attention to the additional provisions, as well:

When such timeouts occur, pthread_cond_timedwait() shall nonetheless release and re-acquire the mutex referenced by mutex, and may consume a condition signal directed concurrently at the condition variable.

Such a release and reaquisition of the mutex / lock is also explicitly specified by C++. It says the effects of wait_until() are:

  • Atomically calls lock.unlock() and blocks on *this .
  • When unblocked, calls lock.lock() (possibly blocking on the lock), then returns.
  • The function will unblock [... after] expiration of the absolute timeout [...]

(C++20 draft 4849, paragraph 32.6.3/18)

Thus, wait_until() unconditionally releases the lock, and even if it unblocks right away on account of timeout, it must then reacquire the lock. This leaves open the possibility that another thread acquires the lock in between, in which case that other thread can hold the lock for an arbitrary amount of time, delaying the return of the wait_until() . This might be a more serious problem for your implementation plan than uncertainty about the method's return value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM