The Pedigree Project 0.1
TimerHandlerRegistry.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
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"
17
18TimerHandlerRegistry::TimerHandlerRegistry()
19 : m_Handlers(),
20 m_ActiveDispatches(),
21 m_DispatchWaiters(),
22 m_HandlerLock(false)
23#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
24 ,
25 m_HandlerPinHook(nullptr),
26 m_HandlerPrePinHook(nullptr),
27 m_HandlerHazardClaimHook(nullptr),
28 m_HandlerAtomicDrainHook(nullptr)
29#endif
30{
31}
32
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);
37}
38
39size_t TimerHandlerRegistry::generationOf(size_t publication) {
40 return publication >> GenerationShift;
41}
42
43TimerHandlerRegistry::SlotMode TimerHandlerRegistry::modeOf(size_t publication) {
44 return static_cast<SlotMode>(publication & ModeMask);
45}
46
47bool TimerHandlerRegistry::selfRemovalOf(size_t publication) {
48 return publication & SelfRemovalBit;
49}
50
51bool TimerHandlerRegistry::synchronousDrainOf(size_t publication) {
52 return publication & SynchronousDrainBit;
53}
54
55bool TimerHandlerRegistry::retireSlot(HandlerSlot& slot, size_t expectedPublication,
56 TimerHandler* expectedHandler) {
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)) {
61 return false;
62 }
63
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),
68 __ATOMIC_RELEASE);
69 return true;
70}
71
72TimerHandlerRegistry::ActiveDispatch* TimerHandlerRegistry::publishDispatch(HandlerSlot& slot,
73 void* owner,
74 void* token) {
75 assert(owner);
76 assert(token);
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,
81 __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);
84
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));
90 }
91#endif
92
93 // This store is the callback pin. Cleanup is armed before the
94 // token claim, so abandoning either side of this publication is
95 // recoverable without a separate in-flight counter.
96 __atomic_store_n(&dispatch.slot, &slot, __ATOMIC_SEQ_CST);
97 return &dispatch;
98 }
99 }
100
101 return nullptr;
102}
103
104bool TimerHandlerRegistry::unpublishDispatch(void* token, HandlerSlot& slot,
105 size_t admittedPublication, bool required) {
106 assert(token);
107 bool found = false;
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) {
112 continue;
113 }
114
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) {
119 continue;
120 }
121
122 if (publishedSlot && publishedSlot != &slot) {
123 FATAL_NOLOCK("Timer callback hazard changed slots during release.");
124 return false;
125 }
126
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);
131 found = true;
132 break;
133 }
134
135 if (!found) {
136 if (required) {
137 FATAL_NOLOCK("Timer callback hazard was released more than once.");
138 }
139 return false;
140 }
141
142 if (!committed) {
143 return true;
144 }
145
146 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
147 if (generationOf(publication) != generationOf(admittedPublication)) {
148 // A remover won before a partial hazard publication committed and the
149 // slot has since been reused. This stale dispatch must not retire the
150 // new generation.
151 return true;
152 }
153
154 const SlotMode mode = modeOf(publication);
155 if (mode != SlotMode::Draining && mode != SlotMode::Deferred && !selfRemovalOf(publication)) {
156 // The pin was cleared before this publication sample. A later remover
157 // will see that release in its own hazard scan; only an existing drain
158 // needs callback-return help, so ordinary ticks need no table scan.
159 return true;
160 }
161 if (hasActiveDispatch(slot)) {
162 return true;
163 }
164
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)) {
169 return true;
170 }
171
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);
176 if (handler) {
177 retireSlot(slot, finalPublication, handler);
178 }
179 }
180
181 if (synchronousDrain) {
182 guard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(&slot, drainGeneration));
183 }
184 return true;
185}
186
187void TimerHandlerRegistry::abandonDispatch(void* context) {
188 DispatchCleanup* dispatch = reinterpret_cast<DispatchCleanup*>(context);
189 dispatch->registry->unpublishDispatch(dispatch, *dispatch->slot, dispatch->publication, false);
190}
191
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);
196 if (!token) {
197 continue;
198 }
199
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) {
204 return true;
205 }
206 }
207 return false;
208}
209
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);
217 if (!token) {
218 continue;
219 }
220
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) {
226 continue;
227 }
228
229 if (dispatchOwner == owner && slot) {
230 callbackContext = true;
231 foundTarget |= slot == target;
232 }
233 }
234 return foundTarget;
235}
236
237void* TimerHandlerRegistry::currentDispatchOwner() {
238 ProcessorInformation& information = Processor::information();
239 Thread* thread = information.getCurrentThread();
240 return thread ? static_cast<void*>(thread) : static_cast<void*>(&information);
241}
242
243bool TimerHandlerRegistry::registerHandler(TimerHandler* handler) {
244 if (!handler) {
245 return false;
246 }
247
248 LockGuard<Spinlock> guard(m_HandlerLock);
249 for (size_t i = 0; i < MaxHandlerSlots; ++i) {
250 HandlerSlot& slot = m_Handlers[i];
251 while (true) {
252 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_SEQ_CST);
253 if (__atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE) != handler) {
254 break;
255 }
256
257 const SlotMode mode = modeOf(publication);
258 if (mode == SlotMode::Retiring) {
260 continue;
261 }
262 if (mode == SlotMode::Empty) {
263 break;
264 }
265 const bool abandonedAtomicDrain = mode == SlotMode::Draining && selfRemovalOf(publication) &&
266 !synchronousDrainOf(publication);
267 if (mode != SlotMode::Deferred &&
268 !(mode == SlotMode::Enabled && selfRemovalOf(publication)) && !abandonedAtomicDrain) {
269 return false;
270 }
271
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)) {
276 return true;
277 }
278 }
279 }
280
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),
289 __ATOMIC_SEQ_CST);
290 return true;
291 }
292 }
293
294 return false;
295}
296
298 if (!handler) {
299 return false;
300 }
301
302 void* owner = currentDispatchOwner();
303 Thread* current = Processor::information().getCurrentThread();
304 const bool canYield = current && Processor::getInterrupts();
305 bool callbackContext = false;
306 findCurrentDispatch(owner, nullptr, callbackContext);
307
308 // A callback must not arm TerminationDeferral even when a hosted test
309 // invokes dispatch with interrupts enabled.
310 if (!canYield || callbackContext) {
311 for (size_t i = 0; i < MaxHandlerSlots; ++i) {
312 HandlerSlot& candidate = m_Handlers[i];
313 while (true) {
314 size_t publication = __atomic_load_n(&candidate.publication, __ATOMIC_SEQ_CST);
315 if (__atomic_load_n(&candidate.handler, __ATOMIC_ACQUIRE) != handler) {
316 break;
317 }
318
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)) {
324 return false;
325 }
326
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)) {
333 return false;
334 }
335 continue;
336 }
337
338 if (mode != SlotMode::Enabled || selfRemovalOf(publication)) {
339 return false;
340 }
341
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)) {
346 continue;
347 }
348
349#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
350 HandlerAtomicDrainHook atomicDrainHook =
351 __atomic_load_n(&m_HandlerAtomicDrainHook, __ATOMIC_ACQUIRE);
352 if (atomicDrainHook) {
353 atomicDrainHook(handler);
354 }
355#endif
356
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)) {
366 // This atomic drainer has abandoned removal. Leave the
367 // callback revivable without discarding the concurrent
368 // self-removal request.
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);
374 }
375 return false;
376 }
377
378 return retireSlot(candidate, drainingPublication, handler);
379 }
380 }
381
382 return false;
383 }
384
385 TerminationDeferral terminationDeferral;
386
387 m_HandlerLock.acquire();
388
389 HandlerSlot* slot = nullptr;
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;
398 break;
399 }
400 }
401
402 if (!slot) {
403 m_HandlerLock.release();
404 return false;
405 }
406
407 size_t drainingPublication = 0;
408 while (true) {
409 const SlotMode mode = modeOf(publication);
410 if (mode == SlotMode::Retiring) {
412 publication = __atomic_load_n(&slot->publication, __ATOMIC_SEQ_CST);
413 continue;
414 }
415 if (mode == SlotMode::Empty) {
416 const bool retired = __atomic_load_n(&slot->handler, __ATOMIC_ACQUIRE) != handler;
417 m_HandlerLock.release();
418 return retired;
419 }
420 if (mode != SlotMode::Enabled && mode != SlotMode::Deferred) {
421 m_HandlerLock.release();
422 return false;
423 }
424
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)) {
430 break;
431 }
432 publication = expectedPublication;
433 }
434
435 m_HandlerLock.release();
436
437 const size_t drainGeneration = generationOf(drainingPublication);
438 while (true) {
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) {
443 return true;
444 }
445
446 const SlotMode finalMode = modeOf(finalPublication);
447 if (finalMode == SlotMode::Empty) {
448 return finalHandler != handler;
449 }
450 if (finalHandler != handler || !synchronousDrainOf(finalPublication) ||
451 (finalMode != SlotMode::Draining && finalMode != SlotMode::Deferred)) {
452 return false;
453 }
454 if (!hasActiveDispatch(*slot)) {
455 if (retireSlot(*slot, finalPublication, handler)) {
456 return true;
457 }
458 continue;
459 }
460
461 const WaitQueue::WakeReason reason =
462 guard.waitForCompletion(WaitQueue::Channel(slot, drainGeneration), Thread::CallbackDrain,
463 reinterpret_cast<uintptr_t>(handler));
464 (void)reason;
465 }
466}
467
468bool TimerHandlerRegistry::dispatch(uint64_t delta, TimerHandler* onlyHandler) {
469 bool admitted = false;
470
471 for (size_t i = 0; i < MaxHandlerSlots; ++i) {
472 HandlerSlot& slot = m_Handlers[i];
473 const size_t publication = __atomic_load_n(&slot.publication, __ATOMIC_ACQUIRE);
474 if (modeOf(publication) != SlotMode::Enabled) {
475 continue;
476 }
477
478 TimerHandler* handler = __atomic_load_n(&slot.handler, __ATOMIC_ACQUIRE);
479 if (!handler || (onlyHandler && handler != onlyHandler)) {
480 continue;
481 }
482
483#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
484 HandlerPrePinHook prePinHook = __atomic_load_n(&m_HandlerPrePinHook, __ATOMIC_ACQUIRE);
485 if (prePinHook) {
486 prePinHook(handler);
487 }
488#endif
489
490 void* owner = currentDispatchOwner();
491 Thread* thread = Processor::information().getCurrentThread();
492 DispatchCleanup dispatchCleanup(this, &slot, owner, publication);
493 if (thread) {
494 // Cleanup is visible before even the hazard-table token is
495 // claimed. A no-return callback, nested exception, or timeout
496 // restore can therefore abandon every later publication point.
497 thread->armAtomicStateCleanup(dispatchCleanup.cleanup, abandonDispatch, &dispatchCleanup);
498 }
499
500 ActiveDispatch* activeDispatch = publishDispatch(slot, owner, &dispatchCleanup);
501 if (!activeDispatch) {
502 if (thread) {
503 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
504 }
505 FATAL_NOLOCK("Timer callback hazard table exhausted.");
506 return admitted;
507 }
508
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);
512 if (thread) {
513 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
514 }
515 continue;
516 }
517
518 admitted = true;
519
520#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
521 HandlerPinHook hook = __atomic_load_n(&m_HandlerPinHook, __ATOMIC_ACQUIRE);
522 if (hook) {
523 hook(handler);
524 }
525#endif
526
527 // Keep callback entry and return independent of ordinary deferred-scope
528 // state: a timer interrupt can arrive while that state is being
529 // mutated.
530 handler->timer(delta);
531 unpublishDispatch(&dispatchCleanup, slot, publication, true);
532 if (thread) {
533 thread->disarmAtomicStateCleanup(dispatchCleanup.cleanup);
534 }
535 }
536
537 return admitted;
538}
539
541 LockGuard<Spinlock> guard(m_HandlerLock);
542 for (size_t i = 0; i < MaxHandlerSlots; ++i) {
543 HandlerSlot& slot = m_Handlers[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),
549 __ATOMIC_RELEASE);
550 }
551
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));
556 }
557}
558
559#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
560void TimerHandlerRegistry::setHandlerPinHook(HandlerPinHook hook) {
561 __atomic_store_n(&m_HandlerPinHook, hook, __ATOMIC_RELEASE);
562}
563
564void TimerHandlerRegistry::setHandlerPrePinHook(HandlerPrePinHook hook) {
565 __atomic_store_n(&m_HandlerPrePinHook, hook, __ATOMIC_RELEASE);
566}
567
568void TimerHandlerRegistry::setHandlerHazardClaimHook(HandlerHazardClaimHook hook) {
569 __atomic_store_n(&m_HandlerHazardClaimHook, hook, __ATOMIC_RELEASE);
570}
571
572void TimerHandlerRegistry::setHandlerAtomicDrainHook(HandlerAtomicDrainHook hook) {
573 __atomic_store_n(&m_HandlerAtomicDrainHook, hook, __ATOMIC_RELEASE);
574}
575
576void TimerHandlerRegistry::withMutationLockForTest(MutationLockHook hook) {
577 m_HandlerLock.acquire();
578 if (hook) {
579 hook();
580 }
581 m_HandlerLock.release();
582}
583
584size_t TimerHandlerRegistry::activeDispatchCountForTest(TimerHandler* handler) {
585 size_t count = 0;
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);
589 if (!token) {
590 continue;
591 }
592 const size_t generation = __atomic_load_n(&dispatch.generation, __ATOMIC_ACQUIRE);
593 HandlerSlot* slot = __atomic_load_n(&dispatch.slot, __ATOMIC_SEQ_CST);
594 TimerHandler* activeHandler =
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) {
598 ++count;
599 }
600 }
601 return count;
602}
603
604size_t TimerHandlerRegistry::claimedDispatchCountForTest() {
605 size_t count = 0;
606 for (size_t i = 0; i < MaxActiveDispatches; ++i) {
607 if (__atomic_load_n(&m_ActiveDispatches[i].token, __ATOMIC_ACQUIRE)) {
608 ++count;
609 }
610 }
611 return count;
612}
613#endif
static bool getInterrupts()
static ProcessorInformation & information()
static void pause()
void release()
Definition Spinlock.cc:161
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:35
bool dispatch(uint64_t delta, TimerHandler *onlyHandler=nullptr)
bool unregisterHandler(TimerHandler *handler)
virtual void timer(uint64_t delta)=0