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.handler,
nullptr, __ATOMIC_RELEASE);
66 __atomic_store_n(&slot.publication,
67 makePublication(generationOf(retiringPublication), SlotMode::Empty),
77 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
78 ActiveDispatch&
dispatch = m_ActiveDispatches[i];
79 void* expectedToken =
nullptr;
80 if (__atomic_compare_exchange_n(&
dispatch.token, &expectedToken, token,
false, __ATOMIC_SEQ_CST,
82 __atomic_add_fetch(&
dispatch.generation,
static_cast<size_t>(1), __ATOMIC_ACQ_REL);
83 __atomic_store_n(&
dispatch.owner, owner, __ATOMIC_RELAXED);
85#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
86 HandlerHazardClaimHook hazardClaimHook =
87 __atomic_load_n(&m_HandlerHazardClaimHook, __ATOMIC_ACQUIRE);
88 if (hazardClaimHook) {
89 hazardClaimHook(__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE));
96 __atomic_store_n(&
dispatch.slot, &slot, __ATOMIC_SEQ_CST);
104bool TimerHandlerRegistry::unpublishDispatch(
void* token, HandlerSlot& slot,
105 size_t admittedPublication,
bool required) {
108 bool committed =
false;
109 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
110 ActiveDispatch&
dispatch = m_ActiveDispatches[i];
111 if (__atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) != token) {
115 const size_t generation = __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE);
116 HandlerSlot* publishedSlot = __atomic_load_n(&
dispatch.slot, __ATOMIC_SEQ_CST);
117 if (__atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) != token ||
118 __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE) != generation) {
122 if (publishedSlot && publishedSlot != &slot) {
123 FATAL_NOLOCK(
"Timer callback hazard changed slots during release.");
127 committed = publishedSlot == &slot;
128 __atomic_store_n(&
dispatch.slot,
nullptr, __ATOMIC_SEQ_CST);
129 __atomic_store_n(&
dispatch.owner,
nullptr, __ATOMIC_RELAXED);
130 __atomic_store_n(&
dispatch.token,
nullptr, __ATOMIC_RELEASE);
137 FATAL_NOLOCK(
"Timer callback hazard was released more than once.");
146 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
147 if (generationOf(publication) != generationOf(admittedPublication)) {
154 const SlotMode mode = modeOf(publication);
155 if (mode != SlotMode::Draining && mode != SlotMode::Deferred && !selfRemovalOf(publication)) {
161 if (hasActiveDispatch(slot)) {
165 const size_t drainGeneration = generationOf(publication);
166 auto guard = m_DispatchWaiters.acquire();
167 const size_t finalPublication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
168 if (generationOf(finalPublication) != drainGeneration || hasActiveDispatch(slot)) {
172 const bool synchronousDrain = synchronousDrainOf(finalPublication);
173 if (modeOf(finalPublication) == SlotMode::Deferred ||
174 (selfRemovalOf(finalPublication) && !synchronousDrain)) {
175 TimerHandler* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
177 retireSlot(slot, finalPublication, handler);
181 if (synchronousDrain) {
182 guard.wakeAll(WaitQueue::WakeReason::Signalled,
WaitQueue::Channel(&slot, drainGeneration));
187void TimerHandlerRegistry::abandonDispatch(
void* context) {
188 DispatchCleanup*
dispatch =
reinterpret_cast<DispatchCleanup*
>(context);
192bool TimerHandlerRegistry::hasActiveDispatch(HandlerSlot& target)
const {
193 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
194 const ActiveDispatch&
dispatch = m_ActiveDispatches[i];
195 void* token = __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE);
200 const size_t generation = __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE);
201 HandlerSlot* slot = __atomic_load_n(&
dispatch.slot, __ATOMIC_SEQ_CST);
202 if (slot == &target && __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) == token &&
203 __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
210bool TimerHandlerRegistry::findCurrentDispatch(
void* owner, HandlerSlot* target,
211 bool& callbackContext)
const {
212 callbackContext =
false;
213 bool foundTarget =
false;
214 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
215 const ActiveDispatch&
dispatch = m_ActiveDispatches[i];
216 void* token = __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE);
221 const size_t generation = __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE);
222 void* dispatchOwner = __atomic_load_n(&
dispatch.owner, __ATOMIC_RELAXED);
223 HandlerSlot* slot = __atomic_load_n(&
dispatch.slot, __ATOMIC_SEQ_CST);
224 if (__atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) != token ||
225 __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE) != generation) {
229 if (dispatchOwner == owner && slot) {
230 callbackContext =
true;
231 foundTarget |= slot == target;
237void* TimerHandlerRegistry::currentDispatchOwner() {
239 Thread* thread = information.getCurrentThread();
240 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
243bool TimerHandlerRegistry::registerHandler(
TimerHandler* handler) {
249 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
250 HandlerSlot& slot = m_Handlers[i];
252 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
253 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
257 const SlotMode mode = modeOf(publication);
258 if (mode == SlotMode::Retiring) {
262 if (mode == SlotMode::Empty) {
265 const bool abandonedAtomicDrain = mode == SlotMode::Draining && selfRemovalOf(publication) &&
266 !synchronousDrainOf(publication);
267 if (mode != SlotMode::Deferred &&
268 !(mode == SlotMode::Enabled && selfRemovalOf(publication)) && !abandonedAtomicDrain) {
272 size_t expectedPublication = publication;
273 if (__atomic_compare_exchange_n(&slot.publication, &expectedPublication,
274 makePublication(generationOf(publication), SlotMode::Enabled),
275 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
281 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
282 HandlerSlot& slot = m_Handlers[i];
283 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
284 if (modeOf(publication) == SlotMode::Empty &&
285 !__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE)) {
286 const size_t generation = generationOf(publication) + 1;
287 __atomic_store_n(&slot.handler, handler, __ATOMIC_RELEASE);
288 __atomic_store_n(&slot.publication, makePublication(generation, SlotMode::Enabled),
302 void* owner = currentDispatchOwner();
305 bool callbackContext =
false;
306 findCurrentDispatch(owner,
nullptr, callbackContext);
310 if (!canYield || callbackContext) {
311 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
314 size_t publication = __atomic_load_n(&candidate.publication, __ATOMIC_SEQ_CST);
315 if (__atomic_load_n(&candidate.handler, __ATOMIC_ACQUIRE) != handler) {
319 const SlotMode mode = modeOf(publication);
320 bool currentTargetDispatch =
false;
321 const bool selfUnregister = findCurrentDispatch(owner, &candidate, currentTargetDispatch);
322 if (selfUnregister) {
323 if (mode == SlotMode::Empty || mode == SlotMode::Retiring || selfRemovalOf(publication)) {
327 const SlotMode deferredMode = mode == SlotMode::Enabled ? SlotMode::Deferred : mode;
328 size_t expectedPublication = publication;
329 if (__atomic_compare_exchange_n(&candidate.publication, &expectedPublication,
330 makePublication(generationOf(publication), deferredMode,
331 true, synchronousDrainOf(publication)),
332 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
338 if (mode != SlotMode::Enabled || selfRemovalOf(publication)) {
342 const size_t drainingPublication =
343 makePublication(generationOf(publication), SlotMode::Draining);
344 if (!__atomic_compare_exchange_n(&candidate.publication, &publication, drainingPublication,
345 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
349#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
350 HandlerAtomicDrainHook atomicDrainHook =
351 __atomic_load_n(&m_HandlerAtomicDrainHook, __ATOMIC_ACQUIRE);
352 if (atomicDrainHook) {
353 atomicDrainHook(handler);
357 if (hasActiveDispatch(candidate)) {
358 size_t expectedPublication = drainingPublication;
359 const bool reopened = __atomic_compare_exchange_n(
360 &candidate.publication, &expectedPublication,
361 makePublication(generationOf(drainingPublication), SlotMode::Enabled,
362 selfRemovalOf(drainingPublication)),
363 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
364 if (!reopened && modeOf(expectedPublication) == SlotMode::Draining &&
365 selfRemovalOf(expectedPublication) && !synchronousDrainOf(expectedPublication)) {
369 size_t markedDraining = expectedPublication;
370 __atomic_compare_exchange_n(
371 &candidate.publication, &markedDraining,
372 makePublication(generationOf(expectedPublication), SlotMode::Deferred,
true),
false,
373 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
378 return retireSlot(candidate, drainingPublication, handler);
390 size_t publication = 0;
391 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
392 const size_t candidatePublication =
393 __atomic_load_n(&m_Handlers[i].publication, __ATOMIC_SEQ_CST);
394 if (modeOf(candidatePublication) != SlotMode::Empty &&
395 __atomic_load_n(&m_Handlers[i].handler, __ATOMIC_ACQUIRE) == handler) {
396 slot = &m_Handlers[i];
397 publication = candidatePublication;
407 size_t drainingPublication = 0;
409 const SlotMode mode = modeOf(publication);
410 if (mode == SlotMode::Retiring) {
412 publication = __atomic_load_n(&slot->publication, __ATOMIC_SEQ_CST);
415 if (mode == SlotMode::Empty) {
416 const bool retired = __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) != handler;
420 if (mode != SlotMode::Enabled && mode != SlotMode::Deferred) {
425 drainingPublication = makePublication(generationOf(publication), SlotMode::Draining,
426 selfRemovalOf(publication),
true);
427 size_t expectedPublication = publication;
428 if (__atomic_compare_exchange_n(&slot->publication, &expectedPublication, drainingPublication,
429 false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) {
432 publication = expectedPublication;
437 const size_t drainGeneration = generationOf(drainingPublication);
439 auto guard = m_DispatchWaiters.acquire();
440 const size_t finalPublication = __atomic_load_n(&slot->publication, __ATOMIC_SEQ_CST);
441 TimerHandler* finalHandler = __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE);
442 if (generationOf(finalPublication) != drainGeneration) {
446 const SlotMode finalMode = modeOf(finalPublication);
447 if (finalMode == SlotMode::Empty) {
448 return finalHandler != handler;
450 if (finalHandler != handler || !synchronousDrainOf(finalPublication) ||
451 (finalMode != SlotMode::Draining && finalMode != SlotMode::Deferred)) {
454 if (!hasActiveDispatch(*slot)) {
455 if (retireSlot(*slot, finalPublication, handler)) {
461 const WaitQueue::WakeReason reason =
462 guard.waitForCompletion(
WaitQueue::Channel(slot, drainGeneration), Thread::CallbackDrain,
463 reinterpret_cast<uintptr_t
>(handler));
469 bool admitted =
false;
471 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
473 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_ACQUIRE);
474 if (modeOf(publication) != SlotMode::Enabled) {
478 TimerHandler* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
479 if (!handler || (onlyHandler && handler != onlyHandler)) {
483#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
484 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
490 void* owner = currentDispatchOwner();
497 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
500 ActiveDispatch* activeDispatch = publishDispatch(slot, owner, &dispatchCleanup);
501 if (!activeDispatch) {
503 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
505 FATAL_NOLOCK(
"Timer callback hazard table exhausted.");
509 if (__atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST) != publication ||
510 __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
511 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
513 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
520#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
521 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
530 handler->
timer(delta);
531 unpublishDispatch(&dispatchCleanup, slot, publication,
true);
533 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
542 for (
size_t i = 0; i < MaxHandlerSlots; ++i) {
544 assert(!hasActiveDispatch(slot));
545 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_ACQUIRE);
546 __atomic_store_n(&slot.handler,
nullptr, __ATOMIC_RELEASE);
547 __atomic_store_n(&slot.publication,
548 makePublication(generationOf(publication) + 1, SlotMode::Empty),
552 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
553 assert(!__atomic_load_n(&m_ActiveDispatches[i].token, __ATOMIC_ACQUIRE));
554 assert(!__atomic_load_n(&m_ActiveDispatches[i].owner, __ATOMIC_ACQUIRE));
555 assert(!__atomic_load_n(&m_ActiveDispatches[i].slot, __ATOMIC_ACQUIRE));
559#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
560void TimerHandlerRegistry::setHandlerPinHook(HandlerPinHook hook) {
561 __atomic_store_n(&m_HandlerPinHook, hook, __ATOMIC_RELEASE);
564void TimerHandlerRegistry::setHandlerPrePinHook(HandlerPrePinHook hook) {
565 __atomic_store_n(&m_HandlerPrePinHook, hook, __ATOMIC_RELEASE);
568void TimerHandlerRegistry::setHandlerHazardClaimHook(HandlerHazardClaimHook hook) {
569 __atomic_store_n(&m_HandlerHazardClaimHook, hook, __ATOMIC_RELEASE);
572void TimerHandlerRegistry::setHandlerAtomicDrainHook(HandlerAtomicDrainHook hook) {
573 __atomic_store_n(&m_HandlerAtomicDrainHook, hook, __ATOMIC_RELEASE);
576void TimerHandlerRegistry::withMutationLockForTest(MutationLockHook hook) {
584size_t TimerHandlerRegistry::activeDispatchCountForTest(
TimerHandler* handler) {
586 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
587 ActiveDispatch&
dispatch = m_ActiveDispatches[i];
588 void* token = __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE);
592 const size_t generation = __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE);
593 HandlerSlot* slot = __atomic_load_n(&
dispatch.slot, __ATOMIC_SEQ_CST);
595 slot ? __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) : nullptr;
596 if (activeHandler == handler && __atomic_load_n(&
dispatch.token, __ATOMIC_ACQUIRE) == token &&
597 __atomic_load_n(&
dispatch.generation, __ATOMIC_ACQUIRE) == generation) {
604size_t TimerHandlerRegistry::claimedDispatchCountForTest() {
606 for (
size_t i = 0; i < MaxActiveDispatches; ++i) {
607 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 dispatch(uint64_t delta, TimerHandler *onlyHandler=nullptr)
bool unregisterHandler(TimerHandler *handler)
virtual void timer(uint64_t delta)=0