8#include "pedigree/kernel/LockGuard.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/machine/TimerHandler.h"
11#include "pedigree/kernel/machine/TimerHandlerRegistry.h"
12#include "pedigree/kernel/process/TerminationDeferral.h"
13#include "pedigree/kernel/process/Thread.h"
14#include "pedigree/kernel/processor/Processor.h"
15#include "pedigree/kernel/processor/ProcessorInformation.h"
16#include "pedigree/kernel/utilities/assert.h"
18TimerHandlerRegistry::TimerHandlerRegistry()
23#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
25 m_HandlerPinHook(nullptr),
26 m_HandlerPrePinHook(nullptr),
27 m_HandlerHazardClaimHook(nullptr),
28 m_HandlerAtomicDrainHook(nullptr)
33size_t TimerHandlerRegistry::makePublication(
size_t generation, SlotMode mode,
bool selfRemoval,
34 bool synchronousDrain) {
35 return (generation << GenerationShift) | (selfRemoval ? SelfRemovalBit : 0) |
36 (synchronousDrain ? SynchronousDrainBit : 0) | static_cast<size_t>(mode);
39size_t TimerHandlerRegistry::generationOf(
size_t publication) {
40 return publication >> GenerationShift;
43TimerHandlerRegistry::SlotMode TimerHandlerRegistry::modeOf(
size_t publication) {
44 return static_cast<SlotMode
>(publication & ModeMask);
47bool TimerHandlerRegistry::selfRemovalOf(
size_t publication) {
48 return publication & SelfRemovalBit;
51bool TimerHandlerRegistry::synchronousDrainOf(
size_t publication) {
52 return publication & SynchronousDrainBit;
55bool TimerHandlerRegistry::retireSlot(HandlerSlot& slot,
size_t expectedPublication,
57 const size_t retiringPublication =
58 makePublication(generationOf(expectedPublication), SlotMode::Retiring);
59 if (!__atomic_compare_exchange_n(&slot.publication, &expectedPublication, retiringPublication,
60 false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
64 assert(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == expectedHandler);
65 __atomic_store_n(&slot.deadline, NoDeadline, __ATOMIC_RELEASE);
66 __atomic_store_n(&slot.lastDispatch, NoDeadline, __ATOMIC_RELEASE);
67 __atomic_store_n(&slot.handler,
nullptr, __ATOMIC_RELEASE);
68 __atomic_store_n(&slot.publication,
69 makePublication(generationOf(retiringPublication), SlotMode::Empty),
74bool TimerHandlerRegistry::publishDispatch(DispatchCleanup& cleanup) {
75 HandlerSlot& slot = *cleanup.slot;
76 void* token = &cleanup;
77 assert(cleanup.owner);
78 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
79 ActiveDispatch&
dispatch = m_ActiveDispatches[i];
81 __atomic_store_n(&cleanup.activeDispatch, &
dispatch, __ATOMIC_RELAXED);
82 void* expectedToken =
nullptr;
83 if (__atomic_compare_exchange_n(&
dispatch.token, &expectedToken, token,
false, __ATOMIC_SEQ_CST,
85 __atomic_add_fetch(&
dispatch.generation,
static_cast<size_t>(1), __ATOMIC_ACQ_REL);
86 __atomic_store_n(&
dispatch.owner, cleanup.owner, __ATOMIC_RELAXED);
88#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
89 HandlerHazardClaimHook hazardClaimHook =
90 __atomic_load_n(&m_HandlerHazardClaimHook, __ATOMIC_ACQUIRE);
91 if (hazardClaimHook) {
92 hazardClaimHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE));
99 __atomic_store_n(&
dispatch.slot, &slot, __ATOMIC_SEQ_CST);
107bool TimerHandlerRegistry::unpublishDispatch(DispatchCleanup& cleanup,
bool required) {
108 ActiveDispatch*
dispatch = __atomic_load_n(&cleanup.activeDispatch, __ATOMIC_RELAXED);
109 void* token = &cleanup;
110 if (!
dispatch || __atomic_load_n(&
dispatch->token, __ATOMIC_ACQUIRE) != token) {
112 FATAL_NOLOCK(
"Timer callback hazard was released more than once.");
117 const size_t generation = __atomic_load_n(&
dispatch->generation, __ATOMIC_ACQUIRE);
118 HandlerSlot* publishedSlot = __atomic_load_n(&
dispatch->slot, __ATOMIC_SEQ_CST);
119 if (__atomic_load_n(&
dispatch->token, __ATOMIC_ACQUIRE) != token ||
120 __atomic_load_n(&
dispatch->generation, __ATOMIC_ACQUIRE) != generation) {
122 FATAL_NOLOCK(
"Timer callback hazard changed during release.");
127 HandlerSlot& slot = *cleanup.slot;
128 const size_t admittedPublication = cleanup.publication;
129 if (publishedSlot && publishedSlot != &slot) {
130 FATAL_NOLOCK(
"Timer callback hazard changed slots during release.");
134 const bool committed = publishedSlot == &slot;
135 __atomic_store_n(&
dispatch->slot,
nullptr, __ATOMIC_SEQ_CST);
136 __atomic_store_n(&
dispatch->owner,
nullptr, __ATOMIC_RELAXED);
137 __atomic_store_n(&
dispatch->token,
nullptr, __ATOMIC_RELEASE);
143 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
144 if (generationOf(publication) != generationOf(admittedPublication)) {
151 const SlotMode mode = modeOf(publication);
152 if (mode != SlotMode::Draining && mode != SlotMode::Deferred && !selfRemovalOf(publication)) {
158 if (hasActiveDispatch(slot)) {
162 const size_t drainGeneration = generationOf(publication);
163 auto guard = m_DispatchWaiters.acquire();
164 const size_t finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
165 if (generationOf(finalPublication) != drainGeneration || hasActiveDispatch(slot)) {
169 const bool synchronousDrain = synchronousDrainOf(finalPublication);
170 if (modeOf(finalPublication) == SlotMode::Deferred ||
171 (selfRemovalOf(finalPublication) && !synchronousDrain)) {
172 TimerHandler* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
174 retireSlot(slot, finalPublication, handler);
178 if (synchronousDrain) {
179 guard.wakeAll(WaitQueue::WakeReason::Signalled,
WaitQueue::Channel(&slot, drainGeneration));
184void TimerHandlerRegistry::abandonDispatch(
void* context) {
185 DispatchCleanup*
dispatch =
reinterpret_cast<DispatchCleanup*
>(context);
189bool TimerHandlerRegistry::hasActiveDispatch(HandlerSlot& target)
const {
190 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
191 const ActiveDispatch&
dispatch = m_ActiveDispatches[i];
192 void* token = __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE);
197 const size_t generation = __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE);
198 HandlerSlot* slot = __atomic_load_n(&
dispatch.slot, __ATOMIC_SEQ_CST);
199 if (slot == &target && __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) == token &&
200 __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
207bool TimerHandlerRegistry::findCurrentDispatch(
void* owner, HandlerSlot* target,
208 bool& callbackContext)
const {
209 callbackContext =
false;
210 bool foundTarget =
false;
211 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
212 const ActiveDispatch&
dispatch = m_ActiveDispatches[i];
213 void* token = __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE);
218 const size_t generation = __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE);
219 void* dispatchOwner = __atomic_load_n(&
dispatch.owner, __ATOMIC_RELAXED);
220 HandlerSlot* slot = __atomic_load_n(&
dispatch.slot, __ATOMIC_SEQ_CST);
221 if (__atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) != token ||
222 __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE) != generation) {
226 if (dispatchOwner == owner && slot) {
227 callbackContext =
true;
228 foundTarget |= slot == target;
234void* TimerHandlerRegistry::currentDispatchOwner() {
236 Thread* thread = information.getCurrentThread();
237 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
240bool TimerHandlerRegistry::registerHandler(
TimerHandler* handler) {
246 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
247 HandlerSlot& slot = m_Handlers[i];
249 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
250 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
254 const SlotMode mode = modeOf(publication);
255 if (mode == SlotMode::Retiring) {
259 if (mode == SlotMode::Empty) {
262 const bool abandonedAtomicDrain = mode == SlotMode::Draining && selfRemovalOf(publication) &&
263 !synchronousDrainOf(publication);
264 if (mode != SlotMode::Deferred &&
265 !(mode == SlotMode::Enabled && selfRemovalOf(publication)) && !abandonedAtomicDrain) {
269 size_t expectedPublication = publication;
270 __atomic_store_n(&slot.deadline, NoDeadline, __ATOMIC_RELEASE);
271 __atomic_store_n(&slot.lastDispatch, NoDeadline, __ATOMIC_RELEASE);
272 if (__atomic_compare_exchange_n(&slot.publication, &expectedPublication,
273 makePublication(generationOf(publication), SlotMode::Enabled),
274 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
280 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
281 HandlerSlot& slot = m_Handlers[i];
282 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
283 if (modeOf(publication) == SlotMode::Empty &&
284 !__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE)) {
285 const size_t generation = generationOf(publication) + 1;
286 __atomic_store_n(&slot.deadline, NoDeadline, __ATOMIC_RELEASE);
287 __atomic_store_n(&slot.lastDispatch, NoDeadline, __ATOMIC_RELEASE);
288 __atomic_store_n(&slot.handler, handler, __ATOMIC_RELEASE);
289 __atomic_store_n(&slot.publication, makePublication(generation, SlotMode::Enabled),
304 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
306 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
307 if (modeOf(publication) != SlotMode::Enabled ||
308 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
313 __atomic_store_n(&slot.deadline, NoDeadline, __ATOMIC_RELEASE);
314 __atomic_store_n(&slot.lastDispatch, NoDeadline, __ATOMIC_RELEASE);
315 return __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) == publication &&
316 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler;
319 const uint64_t armed = __atomic_load_n(&slot.deadline, __ATOMIC_ACQUIRE);
320 if (__atomic_load_n(&slot.lastDispatch, __ATOMIC_ACQUIRE) == NoDeadline ||
321 (armed == NoDeadline && !hasActiveDispatch(slot))) {
322 __atomic_store_n(&slot.lastDispatch, now, __ATOMIC_RELEASE);
324 __atomic_store_n(&slot.deadline, deadline, __ATOMIC_RELEASE);
325 return __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) == publication &&
326 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) == handler;
332 uint64_t earliest = NoDeadline;
333 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
335 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_ACQUIRE);
336 if (modeOf(publication) != SlotMode::Enabled) {
339 const uint64_t deadline = __atomic_load_n(&slot.deadline, __ATOMIC_ACQUIRE);
340 if (__atomic_load_n(&slot.publication, __ATOMIC_ACQUIRE) == publication &&
341 deadline < earliest) {
353 void* owner = currentDispatchOwner();
356 bool callbackContext =
false;
357 findCurrentDispatch(owner,
nullptr, callbackContext);
361 if (!canYield || callbackContext) {
362 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
365 size_t publication = __atomic_load_n(&candidate.publication, __ATOMIC_SEQ_CST);
366 if (__atomic_load_n(&candidate.handler, __ATOMIC_ACQUIRE) != handler) {
370 const SlotMode mode = modeOf(publication);
371 bool currentTargetDispatch =
false;
372 const bool selfUnregister = findCurrentDispatch(owner, &candidate, currentTargetDispatch);
373 if (selfUnregister) {
374 if (mode == SlotMode::Empty || mode == SlotMode::Retiring || selfRemovalOf(publication)) {
378 const SlotMode deferredMode = mode == SlotMode::Enabled ? SlotMode::Deferred : mode;
379 size_t expectedPublication = publication;
380 if (__atomic_compare_exchange_n(&candidate.publication, &expectedPublication,
381 makePublication(generationOf(publication), deferredMode,
382 true, synchronousDrainOf(publication)),
383 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
389 if (mode != SlotMode::Enabled || selfRemovalOf(publication)) {
393 const size_t drainingPublication =
394 makePublication(generationOf(publication), SlotMode::Draining);
395 if (!__atomic_compare_exchange_n(&candidate.publication, &publication, drainingPublication,
396 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
400#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
401 HandlerAtomicDrainHook atomicDrainHook =
402 __atomic_load_n(&m_HandlerAtomicDrainHook, __ATOMIC_ACQUIRE);
403 if (atomicDrainHook) {
404 atomicDrainHook(handler);
408 if (hasActiveDispatch(candidate)) {
409 size_t expectedPublication = drainingPublication;
410 const bool reopened = __atomic_compare_exchange_n(
411 &candidate.publication, &expectedPublication,
412 makePublication(generationOf(drainingPublication), SlotMode::Enabled,
413 selfRemovalOf(drainingPublication)),
414 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
415 if (!reopened && modeOf(expectedPublication) == SlotMode::Draining &&
416 selfRemovalOf(expectedPublication) && !synchronousDrainOf(expectedPublication)) {
420 size_t markedDraining = expectedPublication;
421 __atomic_compare_exchange_n(
422 &candidate.publication, &markedDraining,
423 makePublication(generationOf(expectedPublication), SlotMode::Deferred,
true),
false,
424 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
429 return retireSlot(candidate, drainingPublication, handler);
441 size_t publication = 0;
442 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
443 const size_t candidatePublication =
444 __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
445 if (modeOf(candidatePublication) != SlotMode::Empty &&
446 __atomic_load_n(&m_Handlers[i].handler, __ATOMIC_ACQUIRE) == handler) {
447 slot = &m_Handlers[i];
448 publication = candidatePublication;
458 size_t drainingPublication = 0;
460 const SlotMode mode = modeOf(publication);
461 if (mode == SlotMode::Retiring) {
463 publication = __atomic_load_n(&slot->publication, __ATOMIC_SEQ_CST);
466 if (mode == SlotMode::Empty) {
467 const bool retired = __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) != handler;
471 if (mode != SlotMode::Enabled && mode != SlotMode::Deferred) {
476 drainingPublication = makePublication(generationOf(publication), SlotMode::Draining,
477 selfRemovalOf(publication),
true);
478 size_t expectedPublication = publication;
479 if (__atomic_compare_exchange_n(&slot->publication, &expectedPublication, drainingPublication,
480 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
483 publication = expectedPublication;
488 const size_t drainGeneration = generationOf(drainingPublication);
490 auto guard = m_DispatchWaiters.acquire();
491 const size_t finalPublication = __atomic_load_n(&slot->publication, __ATOMIC_SEQ_CST);
492 TimerHandler* finalHandler = __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE);
493 if (generationOf(finalPublication) != drainGeneration) {
497 const SlotMode finalMode = modeOf(finalPublication);
498 if (finalMode == SlotMode::Empty) {
499 return finalHandler != handler;
501 if (finalHandler != handler || !synchronousDrainOf(finalPublication) ||
502 (finalMode != SlotMode::Draining && finalMode != SlotMode::Deferred)) {
505 if (!hasActiveDispatch(*slot)) {
506 if (retireSlot(*slot, finalPublication, handler)) {
512 const WaitQueue::WakeReason reason =
513 guard.waitForCompletion(
WaitQueue::Channel(slot, drainGeneration), Thread::CallbackDrain,
514 reinterpret_cast<uintptr_t
>(handler));
520 return dispatchSelected(delta, onlyHandler,
false, 0);
524 return dispatchSelected(0,
nullptr,
true, now);
527bool TimerHandlerRegistry::dispatchSelected(uint64_t delta,
TimerHandler* onlyHandler,
bool dueOnly,
529 bool admitted =
false;
531 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
532 HandlerSlot& slot = m_Handlers[i];
533 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_ACQUIRE);
534 if (modeOf(publication) != SlotMode::Enabled) {
538 TimerHandler* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
539 if (!handler || (onlyHandler && handler != onlyHandler)) {
543 const uint64_t deadline = __atomic_load_n(&slot.deadline, __ATOMIC_ACQUIRE);
544 if (deadline == NoDeadline || deadline > now) {
549#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
550 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
556 void* owner = currentDispatchOwner();
558 DispatchCleanup dispatchCleanup(
this, &slot, owner, publication);
563 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
566 if (!publishDispatch(dispatchCleanup)) {
568 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
570 FATAL_NOLOCK(
"Timer callback hazard table exhausted.");
574 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication ||
575 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
576 unpublishDispatch(dispatchCleanup,
true);
578 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
583 uint64_t callbackDelta = delta;
585 uint64_t deadline = __atomic_load_n(&slot.deadline, __ATOMIC_ACQUIRE);
586 while (deadline != NoDeadline && deadline <= now) {
587 if (__atomic_compare_exchange_n(&slot.deadline, &deadline, NoDeadline,
false,
588 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) {
589 const uint64_t previous = __atomic_exchange_n(&slot.lastDispatch, now, __ATOMIC_ACQ_REL);
590 callbackDelta = previous == NoDeadline || now < previous ? 0 : now - previous;
594 if (deadline == NoDeadline || deadline > now) {
595 unpublishDispatch(dispatchCleanup,
true);
597 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
605#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
606 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
615 handler->
timer(callbackDelta);
616 unpublishDispatch(dispatchCleanup,
true);
618 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
627 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
629 assert(!hasActiveDispatch(slot));
630 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_ACQUIRE);
631 __atomic_store_n(&slot.deadline, NoDeadline, __ATOMIC_RELEASE);
632 __atomic_store_n(&slot.lastDispatch, NoDeadline, __ATOMIC_RELEASE);
633 __atomic_store_n(&slot.handler,
nullptr, __ATOMIC_RELEASE);
634 __atomic_store_n(&slot.publication,
635 makePublication(generationOf(publication) + 1, SlotMode::Empty),
639 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
640 assert(!__atomic_load_n(&m_ActiveDispatches[i].token, __ATOMIC_ACQUIRE));
641 assert(!__atomic_load_n(&m_ActiveDispatches[i].owner, __ATOMIC_ACQUIRE));
642 assert(!__atomic_load_n(&m_ActiveDispatches[i].slot, __ATOMIC_ACQUIRE));
646#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
647void TimerHandlerRegistry::setHandlerPinHook(HandlerPinHook hook) {
648 __atomic_store_n(&m_HandlerPinHook, hook, __ATOMIC_RELEASE);
651void TimerHandlerRegistry::setHandlerPrePinHook(HandlerPrePinHook hook) {
652 __atomic_store_n(&m_HandlerPrePinHook, hook, __ATOMIC_RELEASE);
655void TimerHandlerRegistry::setHandlerHazardClaimHook(HandlerHazardClaimHook hook) {
656 __atomic_store_n(&m_HandlerHazardClaimHook, hook, __ATOMIC_RELEASE);
659void TimerHandlerRegistry::setHandlerAtomicDrainHook(HandlerAtomicDrainHook hook) {
660 __atomic_store_n(&m_HandlerAtomicDrainHook, hook, __ATOMIC_RELEASE);
663void TimerHandlerRegistry::withMutationLockForTest(MutationLockHook hook) {
671size_t TimerHandlerRegistry::activeDispatchCountForTest(
TimerHandler* handler) {
673 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
674 ActiveDispatch&
dispatch = m_ActiveDispatches[i];
675 void* token = __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE);
679 const size_t generation = __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE);
680 HandlerSlot* slot = __atomic_load_n(&
dispatch.slot, __ATOMIC_SEQ_CST);
682 slot ? __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) : nullptr;
683 if (activeHandler == handler && __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) == token &&
684 __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
691size_t TimerHandlerRegistry::claimedDispatchCountForTest() {
693 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
694 if (__atomic_load_n(&m_ActiveDispatches[i].token, __ATOMIC_ACQUIRE)) {
static bool getInterrupts()
static ProcessorInformation & information()
bool acquire(bool recurse=false, bool safe=true)
bool armHandler(TimerHandler *handler, uint64_t deadline, uint64_t now)
uint64_t nextDeadline() const
bool dispatchDue(uint64_t now)
bool dispatch(uint64_t delta, TimerHandler *onlyHandler=nullptr)
bool unregisterHandler(TimerHandler *handler)
virtual void timer(uint64_t delta)=0