条件变量wait_untiltimeout_time过去?

Condition Variable wait_until timeout_time in the past?

如果我调用std::condition_variable::wait_until并传递一个过去的timeout_time,是否保证return std::cv_status::timeout?或者是否有一个简短的 window 可以在其中发送通知,导致条件变量以 std::cv_status::no_timeout 的“正常”方式解除阻塞?

上下文:我正在实现一个定时器收集管理器,并打算在条件变量上使用 wait_until 来实现定时器到期。我想要的语义的最简单实现可能涉及将过去的 timeout_time 传递给 std::condition_variable::wait_until 并立即依赖它 returning std::cv_status::timeout (例如,如果一个计时器在另一个计时器的处理正在进行中)。

请注意,我的标准库 wait_until 的实现遵循 pthread_cond_timedwait,因此 API 做出的任何保证都适用于这种情况。

TL;DR:(i) std::condition_variable::wait_until() 的 return 值在指定的条件下似乎不受 C++ 的保证,尽管类似的 pthreads 函数确实保证在这些条件下会发出超时信号; (ii) 即使在超时时间已经过去的情况下,该方法的 return 也可以延迟任意时间长度; (iii) 该方法的单次执行可能会消耗信号并报告超时。

更详细:

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?

自从引入 <condition_variable> 以来,所有版本的规范都对 return 值表示基本相同的内容,例如:

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

(C++20 草案 4849,第 32.6.3/21 段)

但是,总体规范并未明确定义超时已过期的含义。我倾向于认为在调用时已经过去的超时应该算作“绝对超时 [...]调用方法时的过去,而此方法则没有。我不能排除是故意遗漏的。

由于您标记了 pthreads<condition_variable> API 的设计受到这些规范的强烈影响,<condition_variable> 的 UNIX 实现通常涉及薄包装pthreads 函数,考虑 the specifications for pthread_cond_timedwait() 可能很有启发性。其中:

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.

(强调)

因此 pthread_cond_timedwait() 肯定会 return 发出超时信号的错误,如果在调用该函数时超时时间已经过去,并且 wait_until() 实现会决定该超时根据是否 return cv_status::timeout 会表现类似。

而且,pthread_cond_timedwait() 的成功完成并不意味着到函数 returns 时超时还没有过期,即使函数可以做出这样的保证,线程调用该函数不能安全地假设超时不会在函数 returning 和对其 return 值的测试评估之间到期。

另外请注意附加条款:

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.

这种互斥锁/锁的释放和重新获取也是C++明确规定的。它说 wait_until() 的效果是:

  • 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 草案 4849,第 32.6.3/18 段)

因此,wait_until()无条件释放锁,即使因为超时立即解除阻塞,也必须重新获取锁。这留下了另一个线程在两者之间获取锁的可能性,在这种情况下,另一个线程可以持有锁任意时间,从而延迟 wait_until() 的 return。对于您的实施计划来说,这可能是一个比方法的 return 值的不确定性更严重的问题。