On Thu, 20 Apr 2023 at 01:21, Thiago Macieira via Std-Proposals <std-proposals@lists.isocpp.org> 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.