C++ Logo

std-proposals

Advanced search

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

From: Jonathan Wakely <cxx_at_[hidden]>
Date: Thu, 20 Apr 2023 11:26:40 +0100
On Thu, 20 Apr 2023 at 01:21, Thiago Macieira via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> 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".
>

It could be called native_handle() in that case, and be optional / entirely
impl-defined. But ...


> 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.
>

That seems useful, but POSIX.1-202x is based on C17, so we won't get a new
POSIX based on C2x for several years.


> 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.
>

This should be raised with libc implementers ASAP, and with POSIX so it's
on the radar once work on POSIX.1-202x is finished. I'll talk to some glibc
people and if you want I can start a cross-vendor discussion.

Even if POSIX and libc vendors make that guarantee, is it really meaningful
to convert arbitrary POSIX clock IDs from clockid_t to C time bases?

If I get an int which is not equal to TIME_UTC or TIME_MONOTONIC, can I use
that value with timespec_get? If not, why are we munging POSIX clockid_t
values into an int with a possibly different value? It seems like just
returning a native_handle_type with an impl-defined meaning would be
better. That would allow POSIX-based libraries to return a clockid_t (and
document it as such) and other impls to return a C time base as an int, or
just document that it never returns anything meaningful.

Unless we really want to encourage people to use C's timespec_get in code
that is also using <chrono>, I don't see the point in aligning with
timespec_get instead of aligning with POSIX. Your actual use case requires
a clockid_t, wouldn't it be better to get that, without having to perform
some conversion from int to clockid_t based on a convention we hope will
get into a future POSIX standard?



>
> 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.
>

That is a benefit of using the C time base values, as POSIX doesn't seem to
have an invalid clockid_t value or any restrictions on CLOCK_xxx constants
(e.g. requiring that all valid clock IDs are non-negative).
And for a portable std::lib like libstdc++ it would be difficult to
document what value would be returned to indicate no match (we can't know
which clockid_t values are invalid on every OS we support).


> 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.
>

Agreed.


>
> 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.
>

Agreed.


>
> 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
> code.
>
> 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.
>
>

Received on 2023-04-20 10:26:55