C++ Logo

std-proposals

Advanced search

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

From: Andrey Semashev <andrey.semashev_at_[hidden]>
Date: Thu, 20 Apr 2023 10:45:26 +0300
I wanted to say that I faced this problem as well, and my solution was
to define clock traits that, among other things, allowed to know the
clockid_t corresponding to a given chrono clock. Also, in that code base
we defined our own clock types that explicitly use clock_gettime with
various clock id constants, and we don't use std::chrono clocks at all.
Granted, that code base is mostly Linux-only, and definitely not
compatible to Windows. It may run on other POSIX systems, but it was
never tested.

On 4/20/23 03:20, Thiago Macieira via Std-Proposals 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();

I think, it would be better to return clockid_t, and define the type to
int or whatever on platforms that don't define it yet. Returning int
would be suboptimal on (or incompatible with) platforms that define
clockid_t to a different type. It should be strongly implied (perhaps,
in a non-normative note) that this clockid_t type is intended to
correspond to POSIX clockid_t, and the set of supported values matches
the CLOCK_* constants.

Also, clock_id() name seems more appropriate, as the clock type does not
only affect the epoch (base) time point, but also the precision (e.g.
*_COARSE clocks) and time progression (e.g. *_CPUTIME_ID clocks).

Perhaps, it would be even better to add a static constant to a clock
type rather than a static method. This would allow it to use the id in
template parameters.

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

If the clock id is an optional property of a clock, there should be an
easy way to know if it is supported by a given clock. Using
decltype(clock::clock_id()) with SFINAE is possible, but I would prefer
an easier way. I think, the modern way is to define a new clock concept
that would add a requirement of the new member. There should be a trait
similar to is_clock for detecting whether a type models this new
concept. And at least system_clock and steady_clock should be required
to model this new concept.

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

I can add that atomics/futexes also face a similar problem. For timed
waits, one needs to know the clock id that corresponds to a given time
point. Note that e.g. Linux futex API only supports two clocks (realtime
and monotonic), so if a standard library happens to implement its clocks
using other ids for whatever reason (while still meeting the standard
requirements), those clocks become incompatible with futexes.

All this is to say that there is a significant amount of code where it
is important to know exactly what clock id a std::chrono clock corresponds.

Received on 2023-04-20 07:45:30