20#include "TickSource.h"
21#include "pedigree/kernel/utilities/utility.h"
27#include <sys/syscall.h>
31static_assert(__atomic_always_lock_free(
sizeof(
size_t),
nullptr),
32 "hosted tick expiration accounting must remain signal-safe");
33static_assert(__atomic_always_lock_free(
sizeof(
bool),
nullptr),
34 "hosted tick state publication must remain signal-safe");
36timespec nanosecondsToTimespec(uint64_t nanoseconds) {
38 result.tv_sec = nanoseconds / 1000000000ULL;
39 result.tv_nsec = nanoseconds % 1000000000ULL;
43#if !defined(__linux__)
44uint64_t monotonicNanoseconds() {
46 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
49 return (
static_cast<uint64_t
>(now.tv_sec) * 1000000000ULL) +
static_cast<uint64_t
>(now.tv_nsec);
52int waitFor(pthread_cond_t* condition, pthread_mutex_t* mutex, uint64_t nanoseconds) {
53 timespec delay = nanosecondsToTimespec(nanoseconds);
55 return pthread_cond_timedwait_relative_np(condition, mutex, &delay);
58 if (clock_gettime(CLOCK_REALTIME, &deadline) != 0) {
61 deadline.tv_sec += delay.tv_sec;
62 deadline.tv_nsec += delay.tv_nsec;
63 if (deadline.tv_nsec >= 1000000000L) {
65 deadline.tv_nsec -= 1000000000L;
67 return pthread_cond_timedwait(condition, mutex, &deadline);
73HostedTickSource::HostedTickSource()
76#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
77 m_InjectedExpirations(0),
87 m_IntervalNanoseconds(0),
88 m_PendingExpirations(0),
94 m_HelperStarted(
false),
102HostedTickSource::~HostedTickSource() {
106bool HostedTickSource::prepare(
int signal,
void* owner) {
107 if (__atomic_load_n(&m_Prepared, __ATOMIC_ACQUIRE) || signal <= 0 || !owner) {
114#if defined(__linux__)
115#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
116 __atomic_store_n(&m_InjectedExpirations,
static_cast<size_t>(0), __ATOMIC_RELEASE);
118 const long executionThreadId = ::syscall(SYS_gettid);
119 if (executionThreadId <= 0) {
124 ByteSet(&event, 0,
sizeof(event));
125 event.sigev_notify = SIGEV_THREAD_ID;
126 event.sigev_signo = signal;
127 event.sigev_value.sival_ptr = owner;
128 event._sigev_un._tid =
static_cast<decltype(event._sigev_un._tid)
>(executionThreadId);
129 if (timer_create(CLOCK_MONOTONIC, &event, &m_Timer) != 0) {
133 m_TargetThread = pthread_self();
134 m_IntervalNanoseconds = 0;
135 m_PendingExpirations = 0;
138 m_Delivering =
false;
141 if (pthread_mutex_init(&m_Mutex,
nullptr) != 0) {
144 if (pthread_cond_init(&m_Condition,
nullptr) != 0) {
145 pthread_mutex_destroy(&m_Mutex);
148 if (pthread_create(&m_HelperThread,
nullptr, helperEntry,
this) != 0) {
149 pthread_cond_destroy(&m_Condition);
150 pthread_mutex_destroy(&m_Mutex);
153 m_HelperStarted =
true;
156 __atomic_store_n(&m_Prepared,
true, __ATOMIC_RELEASE);
160bool HostedTickSource::arm(uint64_t intervalNanoseconds) {
161 if (!__atomic_load_n(&m_Prepared, __ATOMIC_ACQUIRE) || !intervalNanoseconds) {
165#if defined(__linux__)
167 ByteSet(&interval, 0,
sizeof(interval));
168 interval.it_interval = nanosecondsToTimespec(intervalNanoseconds);
169 interval.it_value = interval.it_interval;
170 return timer_settime(m_Timer, 0, &interval,
nullptr) == 0;
172 pthread_mutex_lock(&m_Mutex);
173 if (__atomic_load_n(&m_Failed, __ATOMIC_ACQUIRE)) {
174 pthread_mutex_unlock(&m_Mutex);
177 m_IntervalNanoseconds = intervalNanoseconds;
180 pthread_cond_broadcast(&m_Condition);
181 pthread_mutex_unlock(&m_Mutex);
186bool HostedTickSource::disarm() {
187 if (!__atomic_load_n(&m_Prepared, __ATOMIC_ACQUIRE)) {
191#if defined(__linux__)
192#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
193 __atomic_store_n(&m_InjectedExpirations,
static_cast<size_t>(0), __ATOMIC_RELEASE);
196 ByteSet(&disarmed, 0,
sizeof(disarmed));
197 return timer_settime(m_Timer, 0, &disarmed,
nullptr) == 0;
199 pthread_mutex_lock(&m_Mutex);
202 pthread_cond_broadcast(&m_Condition);
203 while (m_Delivering) {
204 pthread_cond_wait(&m_Condition, &m_Mutex);
206 __atomic_store_n(&m_PendingExpirations,
static_cast<size_t>(0), __ATOMIC_RELEASE);
207 const bool success = !__atomic_load_n(&m_Failed, __ATOMIC_ACQUIRE);
208 pthread_mutex_unlock(&m_Mutex);
213void HostedTickSource::destroy() {
214 if (!__atomic_load_n(&m_Prepared, __ATOMIC_ACQUIRE)) {
218#if defined(__linux__)
219 timer_delete(m_Timer);
222 pthread_mutex_lock(&m_Mutex);
224 pthread_cond_broadcast(&m_Condition);
225 pthread_mutex_unlock(&m_Mutex);
226 if (m_HelperStarted) {
227 pthread_join(m_HelperThread,
nullptr);
228 m_HelperStarted =
false;
230 pthread_cond_destroy(&m_Condition);
231 pthread_mutex_destroy(&m_Mutex);
234 __atomic_store_n(&m_Prepared,
false, __ATOMIC_RELEASE);
239HostedTickSource::TakeResult HostedTickSource::takeExpirations(
const siginfo_t* info,
240 size_t& expirations) {
242 if (!__atomic_load_n(&m_Prepared, __ATOMIC_ACQUIRE) || !info || info->si_signo != m_Signal) {
243 return TakeResult::NotSource;
246#if defined(__linux__)
247 if (info->si_code != SI_TIMER || info->si_value.sival_ptr != m_Owner) {
248#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
250 __atomic_exchange_n(&m_InjectedExpirations,
static_cast<size_t>(0), __ATOMIC_ACQ_REL);
252 return TakeResult::Expirations;
255 return TakeResult::NotSource;
257 if (info->si_overrun < 0) {
258 return TakeResult::Invalid;
260 expirations =
static_cast<size_t>(info->si_overrun) + 1;
262 if (__atomic_load_n(&m_Failed, __ATOMIC_ACQUIRE)) {
263 return TakeResult::Invalid;
266 __atomic_exchange_n(&m_PendingExpirations,
static_cast<size_t>(0), __ATOMIC_ACQ_REL);
268 return TakeResult::Expirations;
271#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
272bool HostedTickSource::queueExpirationForTest() {
273 if (!__atomic_load_n(&m_Prepared, __ATOMIC_ACQUIRE)) {
277#if defined(__linux__)
278 size_t pending = __atomic_load_n(&m_InjectedExpirations, __ATOMIC_ACQUIRE);
280 if (pending == ~
static_cast<size_t>(0)) {
283 if (__atomic_compare_exchange_n(&m_InjectedExpirations, &pending, pending + 1,
false,
284 __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)) {
288 if (pthread_kill(pthread_self(), m_Signal) == 0) {
291 __atomic_fetch_sub(&m_InjectedExpirations, 1, __ATOMIC_ACQ_REL);
294 if (!recordExpirations(1)) {
297 if (pthread_kill(m_TargetThread, m_Signal) == 0) {
300 __atomic_fetch_sub(&m_PendingExpirations, 1, __ATOMIC_ACQ_REL);
306#if !defined(__linux__)
307void* HostedTickSource::helperEntry(
void* source) {
312bool HostedTickSource::recordExpirations(
size_t expirations) {
313 size_t pending = __atomic_load_n(&m_PendingExpirations, __ATOMIC_ACQUIRE);
315 if (expirations > (~
static_cast<size_t>(0) - pending)) {
318 if (__atomic_compare_exchange_n(&m_PendingExpirations, &pending, pending + expirations,
false,
319 __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)) {
325void HostedTickSource::helper() {
326 pthread_mutex_lock(&m_Mutex);
328 while (!m_Stop && !m_Armed) {
329 pthread_cond_wait(&m_Condition, &m_Mutex);
335 const size_t generation = m_Generation;
336 const uint64_t interval = m_IntervalNanoseconds;
337 uint64_t deadline = monotonicNanoseconds() + interval;
338 while (!m_Stop && m_Armed && generation == m_Generation) {
339 const uint64_t now = monotonicNanoseconds();
340 if (now < deadline) {
341 waitFor(&m_Condition, &m_Mutex, deadline - now);
345 const uint64_t elapsed = now - deadline;
346 const uint64_t count64 = 1 + (elapsed / interval);
347 if (count64 >
static_cast<uint64_t
>(~
static_cast<size_t>(0))) {
348 __atomic_store_n(&m_Failed,
true, __ATOMIC_RELEASE);
352 deadline += count64 * interval;
354 pthread_mutex_unlock(&m_Mutex);
356 const bool recorded = recordExpirations(
static_cast<size_t>(count64));
357 const int delivered = recorded ? pthread_kill(m_TargetThread, m_Signal) : EOVERFLOW;
359 pthread_mutex_lock(&m_Mutex);
360 if (delivered != 0) {
361 __atomic_store_n(&m_Failed,
true, __ATOMIC_RELEASE);
364 m_Delivering =
false;
365 pthread_cond_broadcast(&m_Condition);
368 pthread_mutex_unlock(&m_Mutex);