Skip to content

TedLyngmo/CloCXX

Repository files navigation

CloCXX

A header only C++ library with std::chrono compatible clocks satisfying the TrivialClock requirement

Building the clocxx.hpp header in-source

make

Building and installing the clocxx.hpp header out-of-source

This will build clocxx.hpp and install it in ~/.local/include:

cmake --install-prefix ~/.local -S . -B build
cmake --build build
cmake --install build

About

CloCXX comes with a file with recipes named seen_clockid_t. This contains the clockid_t macro name, a bool describing if the clock is steady or not and finally a description of the clock. When building the clocxx.hpp header, each recipe will be checked individually and only working clock definitions will be included in the generated header file. In addition to the typedefs required to be a TrivialClock, each lyn::chrono clock comes with two additional constants:

// The actual clockid_t used:
static constexpr clockid_t clock_id;

// If this clock can use TIMER_ABSTIME in clock_nanosleep:
static constexpr bool can_clock_nanosleep;

While the "steadyness" of each clock is hardcoded in the recipe file, if the clock can use clock_nanosleep is determined while generating the header file. Drop-in replacement functions for std::this_thread::sleep_for and std::this_thread::sleep_until are available in lyn::this_thread that sleeps until an absolute time if the clock supports clock_nanosleep. Note that lyn::this_thread::sleep_for has a Clock template parameter that can be used to specify what clock to use for sleeping.

template<class Clock = std::chrono::steady_clock, class Rep, class Period>
void sleep_for(const std::chrono::duration<Rep, Period>& dur);

template<class Clock, class Duration>
void sleep_until(const std::chrono::time_point<Clock, Duration>& tp);

Note: The header file will only function correctly when used on the platform for which it was generated. Create the file for each individual platform where you want it. If your code is supposed to work on multiple platforms, select clocks that exists on all of them. Do not copy a generated clocxx.hpp file generated for one platform to another platform. Time as you know it may come to an end.

Example clocks that may be generated:

/*
 * monotonic_coarse_clock - A faster but less precise version of
 * CLOCK_MONOTONIC. Use when you need very fast, but not fine-grained
 * timestamps. Requires per-architecture support, and probably also
 * architecture support for this flag in the vdso(7). Linux-specific.
 */
struct monotonic_coarse_clock :
    public detail::clock_base<monotonic_coarse_clock, true, false,
                              CLOCK_MONOTONIC_COARSE> {};

/*
 * monotonic_raw_clock - Similar to CLOCK_MONOTONIC, but provides access to
 * a raw hardware-based time that is not subject to NTP adjustments or the
 * incremental adjustments performed by adjtime(3). This clock does not
 * count time that the system is suspended. Linux-specific.
 */
struct monotonic_raw_clock :
    public detail::clock_base<monotonic_raw_clock, true, false,
                              CLOCK_MONOTONIC_RAW> {};

All clocks share the same base implementation:

namespace detail {
    template<class Clock, bool IsSteady, bool CanClockNanoSleep, clockid_t CLOCK_ID>
    struct clock_base {
        using duration = std::chrono::nanoseconds;
        using rep = duration::rep;
        using period = duration::period;
        using time_point = std::chrono::time_point<Clock, duration>;
        static constexpr bool is_steady = IsSteady;

        static constexpr clockid_t clock_id = CLOCK_ID;
        static constexpr bool can_clock_nanosleep = CanClockNanoSleep;

        static time_point now() noexcept {
            using namespace std;
            timespec tp; // not in std:: until gcc 9
            static_cast<void>(::clock_gettime(CLOCK_ID, &tp));

            return time_point(
                duration(std::chrono::seconds(tp.tv_sec) +
                         std::chrono::nanoseconds(tp.tv_nsec)));
        }
    };
} // namespace detail