pthread_cond_timedwait with timeout in the past slower than it used to be

Jonathan Wakely jwakely.gcc@gmail.com
Wed Nov 18 17:00:05 GMT 2020


On Wed, 18 Nov 2020 at 16:10, Mike Crowe <mac@mcrowe.com> wrote:
> Having explained all that, my question now is "does this matter?"
>
> To help answer, I think the following things are important:
>
> 1. The equivalent change for CLOCK_REALTIME happened many years ago (but
> not long enough ago that it predated the invention of the vdso), and
> presumably no-one complained loudly enough for it to be reverted.
>
> 2. Fixing it makes callers who pass timeouts in the future do unnecessary
> work.
>
> 3. It's not really clear how often production code ends up waiting on
> timeouts in the past. I can come up with loops that calculate their timeout
> at the top before doing some work that might take some time, but even then
> it doesn't seem likely that the wait taking a little bit longer is that
> bad.
>
> 4. Any caller that really cares about this can check the timeout against
> the current time before calling pthread_cond_timedwait themselves.

I discovered this in the context of C++'s std::future, which is used
to pass an asynchronous result between threads. std::future doesn't
provide any way to poll for readiness, so for a consumer to find out
if the producer has made a result available you have to wait with a
short timeout. Using GCC trunk the optimal way to poll is
fut.wait_for(seconds(0)), but this was painfully slow in previous
releases. Using fut.wait_until(abs_time) with an absolute time in the
past was faster. A reliable way to get an absolute time in the past
(without doing a call to find the current time) is to use the epoch,
or the earliest time representable by the system clock:
std::system_clock::time_point::min(). So that's the use case for
waiting with expired times in C++.

That probably isn't relevant in the general case, because most other
forms of synchronization do not need to use a timeout for polling. If
you are waiting with a condition variable, you can just check the
associated predicate. If it's true, don't wait at all. If it's false,
you probably want to wait for a non-zero time, so you wouldn't wait
until a time in the past. I'm not sure there's a good use case for
polling a mutex with pthread_rwlock_timedrdlock when you could just
use pthread_rwlock_tryrdlock instead.

So where there's a regression in real code might be specific to
std::future (and even then it's only because the performance of GCC's
fut.wait_for(0) was bad until recently). If that's true, it would mean
I need to care about the regression, but glibc doesn't.


More information about the Libc-alpha mailing list