7#ifndef STLAB_CONCURRENCY_SYSTEM_TIMER_HPP
8#define STLAB_CONCURRENCY_SYSTEM_TIMER_HPP
34#include <stlab/execution/config.hpp>
46#include <system_error>
56 std::int32_t native_error;
59static_assert(std::is_standard_layout_v<stlab_v2_timer_status>);
78 std::int64_t delay_ns)
noexcept;
82STLAB_EXECUTION_VERSION_NAMESPACE_BEGIN()
90namespace execution_detail {
95inline auto timer_integral_nanoseconds(std::uintmax_t count,
96 std::uintmax_t numerator,
97 std::uintmax_t denominator,
98 std::uintmax_t multiplier = 1) -> std::int64_t {
99 constexpr auto limit =
static_cast<std::uintmax_t
>(std::numeric_limits<std::int64_t>::max());
100 std::uintmax_t quotient = 0;
101 std::uintmax_t remainder = 0;
102 if (count <= std::numeric_limits<std::uintmax_t>::max() / numerator) {
103 const auto product = count * numerator;
104 quotient = product / denominator;
105 remainder = product % denominator;
108 const auto whole = numerator / denominator;
109 const auto fraction = numerator % denominator;
110 for (
int bit = std::numeric_limits<std::uintmax_t>::digits - 1; bit >= 0; --bit) {
111 if (quotient > limit / 2)
112 throw std::overflow_error(
"system_timer delay exceeds signed nanoseconds");
115 auto carry = remainder / denominator;
116 remainder %= denominator;
117 if ((count >> bit) & 1) {
118 if (whole > limit - carry)
119 throw std::overflow_error(
"system_timer delay exceeds signed nanoseconds");
121 remainder += fraction;
122 carry += remainder / denominator;
123 remainder %= denominator;
125 if (carry > limit - quotient)
126 throw std::overflow_error(
"system_timer delay exceeds signed nanoseconds");
130 if (quotient > limit / multiplier)
131 throw std::overflow_error(
"system_timer delay exceeds signed nanoseconds");
132 quotient *= multiplier;
133 const auto rounded_fraction =
134 multiplier == 1 ?
static_cast<std::uintmax_t
>(remainder != 0) :
135 static_cast<std::uintmax_t
>(
136 timer_integral_nanoseconds(remainder, multiplier, denominator));
137 if (rounded_fraction > limit - quotient)
138 throw std::overflow_error(
"system_timer delay exceeds signed nanoseconds");
139 return static_cast<std::int64_t
>(quotient + rounded_fraction);
143template <
typename Rep,
typename Period>
144auto timer_nanoseconds(std::chrono::duration<Rep, Period> duration) -> std::int64_t {
145 if constexpr (std::is_floating_point_v<Rep>) {
146 if (!std::isfinite(duration.count()))
147 throw std::invalid_argument(
"system_timer delay must be finite");
149 if (duration.count() <= 0)
return 0;
150 if constexpr (std::is_integral_v<Rep>) {
151 return timer_integral_nanoseconds(
static_cast<std::uintmax_t
>(duration.count()),
152 Period::num, Period::den, 1000000000);
155 std::ceil(
static_cast<long double>(duration.count()) *
156 static_cast<long double>(Period::num) / Period::den * 1000000000);
158 const auto bound = -
static_cast<long double>(std::numeric_limits<std::int64_t>::min());
159 if (!std::isfinite(value) || value >= bound)
160 throw std::overflow_error(
"system_timer delay exceeds signed nanoseconds");
161 return value < 1 ? 1 : static_cast<std::int64_t>(value);
166inline void submit_system_timer(std::int64_t delay_ns,
task<
void()
noexcept>& f) {
169 f.relocation_invoke(), f.relocation_source(), delay_ns);
170 switch (result.code) {
174 throw std::bad_alloc();
176 throw std::system_error(result.native_error, std::generic_category());
178 throw std::system_error(result.native_error, std::system_category());
180 assert(
false &&
"invalid timer ABI resource status");
186struct system_timer_type {
187 using result_type = void;
196 void operator()(std::chrono::steady_clock::time_point when, task<
void()
noexcept>&& f)
const {
197 const auto now = std::chrono::steady_clock::now();
199 submit_system_timer(0, f);
202 using duration = std::chrono::steady_clock::duration;
203 using unsigned_rep = std::make_unsigned_t<typename duration::rep>;
204 const auto remaining =
static_cast<unsigned_rep
>(when.time_since_epoch().count()) -
205 static_cast<unsigned_rep
>(now.time_since_epoch().count());
208 std::chrono::duration<unsigned_rep, typename duration::period>(remaining)),
221 template <
typename Rep,
typename Period>
222 void operator()(std::chrono::duration<Rep, Period> duration, task<
void()
noexcept>&& f)
const {
223 submit_system_timer(timer_nanoseconds(duration), f);
233inline constexpr auto system_timer = execution_detail::system_timer_type{};
237STLAB_EXECUTION_VERSION_NAMESPACE_END()
void(*)(void *) noexcept stlab_v2_task_proc
Function pointer type submitted through the shared executor ABI.
Definition default_executor.hpp:64
constexpr auto system_timer
Schedules move-only void() noexcept tasks through the process-shared timer service.
Definition system_timer.hpp:233
typename noexcept_deducer< task_, F >::type task
task_ with noexcept deduced from the function type F (e.g. void() vs void() noexcept).
Definition task.hpp:453
Definition default_executor.hpp:50
Definition default_executor.hpp:49
stlab_v2_timer_status stlab_v2_system_timer_submit(const unsigned char *task_abi_guard, const stlab_v2_task_concept *vtable, stlab_v2_task_proc invoke, void *source, std::int64_t delay_ns) noexcept
Accepts a task into the core timer service without propagating exceptions.
Register and run operations that must execute before program exit.
Versioned operation table used to relocate a task target across the shared executor ABI.
Definition task.hpp:64
Resource result: code 0 is success, 1 allocation failure, 2 generic-category error,...
Definition system_timer.hpp:54
Move-only callable wrapper for executor scheduling (task<Signature>).