C++ Logo


Advanced search

[std-proposals] Obtaining the low-level clock used by std::chrono::steady_clock

From: Thiago Macieira <thiago_at_[hidden]>
Date: Wed, 19 Apr 2023 17:20:51 -0700
In refactoring some older Qt code that directly used clock_gettime() to use
std::chrono::steady_clock, I stumbled upon a limitation: I need to pass clock
time points to certain low-level functions. The one in particular I was
looking at is pthread_cond_timedwait(), whose timestamp must match the POSIX
clock set with pthread_condattr_setclock(). I wrote the code assuming that in
all systems std::chrono::steady_clock was the same as CLOCK_MONOTONIC, but
this isn't true in either Darwin systems (libc++ uses CLOCK_MONOTONIC_RAW) or
QNX (no clue what happens there).

Therefore, I'd like to propose that steady_clock add an extra method that
returns the clock ID. It could be as simple as:

    [[pure]] static int clock_base();

1) what should this number be?

Ideally for me, it would be the POSIX clockid_t, so I could directly pass it
to pthread_condattr_setclock() or other POSIX RT functions. But we can't rely
on POSIX RT being supported in the operating system in question (read:
Windows, macOS < 10.12), therefore this function would have to be standardised
as "returns an implementation-specified value".

A more standardisable alternative is to return the same clock constants that
are used by C11's timespec_get(). C11 introduced TIME_UTC and C23 is
introducing the (optional) TIME_MONOTONIC, with a future direction that it may
become mandatory. C11 also says that implementations may provide more clocks.
This would require updating our <ctime> reference up to C23, which is probably
reasonable. This solution would permit standard C and C++ code to have
interoperable time points.

However, the vendors of the C libraries and the C++ ones are seldom the same
and they have different release cycles. Right now, I don't know of any C
library that offers TIME_MONOTONIC. Requiring such an update before deploying
C++26 may not be possible in all systems. Moreover, many codebases would still
need to map from standard C TIME_ constants to POSIX CLOCK_ ones somehow,
unless POSIX provides such a function. Unfortunately, POSIX can't mandate that
they be the same value because CLOCK_REALTIME is often 0, but C11 required
TIME_UTC to be a positive value. POSIX should mandate that the formula
   c_clock - TIME_UTC == posix_clock_id - CLOCK_REALTIME
hold true for all clocks.

If C library implementations commit to this formula, it becomes possible for
C++ standard libraries to anticipate the value that TIME_MONOTONIC will have,
even before it exists.

2) should this be present in other std::chrono clocks?

Yes, I think so. With the C time.h clock bases, the standard clocks can return
0 to indicate that there is no match.

3) should it be mandatory in the Clock requirement?

No. That would mean std::is_clock ought to be updated to check that it exists,
and that would lead existing code that currently defines clocks to start
failing the concept check.

4) should the function be constexpr?

No. The reason for this is that the standard library may have runtime choices
depending on the system, so it can't have a constant value hardcoded in the
application. Additionally, the implementation may want the right to change the
clock source -- there's some discussion about using CLOCK_MONOTONIC_RAW in
libstdc++, for example.

Implementations that want to make it constexpr can do so, understanding it
becomes an ABI choice.

It should be a pure function, meaning its value can't change once known.

5) why don't you use std::condition_variable?

pthread_cond_t is not the only place this would be useful for, it was just the
one I ran into. It may also not be possible to update all uses of pthreads in
existing codebases, such as where such threading primitives are shared with C

Both libstdc++ and libc++ specialise std::condition_variable::wait_until when
the clock is steady_clock, so they can call to pthread_cond_clockwait() with
CLOCK_MONOTONIC and avoid a recalculation. This proposal could allow those
implementations to avoid hardcoding the clock ID, avoiding mistakes when one
portion of a codebase is updated and not the other. This can be seen inside
current libc++: condition_variable::__do_timed_wait() hardcodes
CLOCK_MONOTONIC for all systems, but steady_clock may use different clocks
(fortunately for macOS, it lacks pthread_cond_clockwait, so no mismatch
currently happens). Likewise, libstdc++'s steady_clock::now() has fallback
code paths to system_clock if the library didn't find CLOCK_MONOTONIC, so an
improper upgrade of the library could result in a mismatch with code inlined
in user applications.

Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
   Software Architect - Intel DCAI Cloud Engineering

Received on 2023-04-20 00:20:55