The Pedigree Project 0.1
SchedulerTimerHandlerSlot.h
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#ifndef KERNEL_MACHINE_SCHEDULERTIMERHANDLERSLOT_H
9#define KERNEL_MACHINE_SCHEDULERTIMERHANDLERSLOT_H
10#include <config.h>
11#include <stddef.h>
12
14
15static_assert(__atomic_always_lock_free(sizeof(SchedulerTimerHandler*), nullptr),
16 "scheduler timer-handler publication must be lock-free");
17static_assert(__atomic_always_lock_free(sizeof(size_t), nullptr),
18 "scheduler timer-handler ownership must be lock-free");
19
30 public:
31 static constexpr size_t NoOwner = ~static_cast<size_t>(0);
32
35 public:
36 DispatchGuard() : m_Slot(nullptr), m_Handler(nullptr) {}
37
39 release();
40 }
41
42 SchedulerTimerHandler* handler() const {
43 return m_Handler;
44 }
45
53 void release() {
54 SchedulerTimerHandlerSlot* slot = m_Slot;
55 m_Slot = nullptr;
56 m_Handler = nullptr;
57 if (slot) {
58 slot->completeDispatch();
59 }
60 }
61
62 private:
63 friend class SchedulerTimerHandlerSlot;
64 DispatchGuard(const DispatchGuard&) = delete;
65 DispatchGuard& operator=(const DispatchGuard&) = delete;
66
68 SchedulerTimerHandler* m_Handler;
69 };
70
71 SchedulerTimerHandlerSlot() : m_Handler(nullptr), m_Owner(NoOwner), m_State(Empty) {}
72
74 bool publish(size_t owner, SchedulerTimerHandler* handler) {
75 if (!handler || owner == NoOwner)
76 return false;
77
78 size_t expected = Empty;
79 if (!__atomic_compare_exchange_n(&m_State, &expected, Publishing, false, __ATOMIC_ACQ_REL,
80 __ATOMIC_ACQUIRE)) {
81 return false;
82 }
83
84 __atomic_store_n(&m_Owner, owner, __ATOMIC_RELAXED);
85 __atomic_store_n(&m_Handler, handler, __ATOMIC_RELEASE);
86 __atomic_store_n(&m_State, Published, __ATOMIC_RELEASE);
87 return true;
88 }
89
97 bool unpublish(size_t owner, SchedulerTimerHandler* handler) {
98 if (!handler || owner == NoOwner || __atomic_load_n(&m_Owner, __ATOMIC_ACQUIRE) != owner)
99 return false;
100
101 if (__atomic_load_n(&m_Handler, __ATOMIC_ACQUIRE) != handler)
102 return false;
103
104 // Published with a zero admission count is the only removable state.
105 // A suspended timer frame therefore makes removal fail immediately
106 // instead of deadlocking its own processor while waiting to drain.
107 size_t expected = Published;
108 if (!__atomic_compare_exchange_n(&m_State, &expected, Removing, false, __ATOMIC_ACQ_REL,
109 __ATOMIC_ACQUIRE)) {
110 return false;
111 }
112
113 __atomic_store_n(&m_Handler, static_cast<SchedulerTimerHandler*>(nullptr), __ATOMIC_RELEASE);
114 __atomic_store_n(&m_Owner, NoOwner, __ATOMIC_RELEASE);
115 __atomic_store_n(&m_State, Empty, __ATOMIC_RELEASE);
116 return true;
117 }
118
120 bool beginDispatch(size_t owner, DispatchGuard& guard) {
121 if (guard.m_Slot || owner == NoOwner)
122 return false;
123
124 if (__atomic_load_n(&m_Owner, __ATOMIC_ACQUIRE) != owner) {
125 return false;
126 }
127
128 size_t observed = __atomic_load_n(&m_State, __ATOMIC_ACQUIRE);
129 while ((observed & ModeMask) == Published) {
130 if ((observed & DispatchCountMask) == DispatchCountMask) {
131 return false;
132 }
133
134 const size_t admitted = observed + DispatchIncrement;
135 if (__atomic_compare_exchange_n(&m_State, &observed, admitted, false, __ATOMIC_ACQ_REL,
136 __ATOMIC_ACQUIRE)) {
137 break;
138 }
139 }
140 if ((observed & ModeMask) != Published) {
141 return false;
142 }
143
144 SchedulerTimerHandler* handler = __atomic_load_n(&m_Handler, __ATOMIC_ACQUIRE);
145 if (!handler || __atomic_load_n(&m_Owner, __ATOMIC_ACQUIRE) != owner) {
146 completeDispatch();
147 return false;
148 }
149
150 guard.m_Handler = handler;
151 guard.m_Slot = this;
152 return true;
153 }
154
156 bool isPublished(size_t owner, SchedulerTimerHandler* handler) const {
157 const size_t before = __atomic_load_n(&m_State, __ATOMIC_ACQUIRE);
158 if ((before & ModeMask) != Published)
159 return false;
160 return handler && __atomic_load_n(&m_Owner, __ATOMIC_ACQUIRE) == owner &&
161 __atomic_load_n(&m_Handler, __ATOMIC_ACQUIRE) == handler &&
162 __atomic_load_n(&m_State, __ATOMIC_ACQUIRE) == before;
163 }
164
166 size_t activeDispatches() const {
167 const size_t state = __atomic_load_n(&m_State, __ATOMIC_ACQUIRE);
168 return (state & ModeMask) == Published ? (state & DispatchCountMask) / DispatchIncrement : 0;
169 }
170
171#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
173 SchedulerTimerHandler* publishedHandlerForTest(size_t owner) const {
174 SchedulerTimerHandler* handler = __atomic_load_n(&m_Handler, __ATOMIC_ACQUIRE);
175 return isPublished(owner, handler) ? handler : nullptr;
176 }
177#endif
178
179 private:
180 enum State : size_t {
181 Empty,
182 Publishing,
183 Published,
184 Removing,
185 };
186
187 static constexpr size_t ModeMask = 3;
188 static constexpr size_t DispatchIncrement = ModeMask + 1;
189 static constexpr size_t DispatchCountMask = ~ModeMask;
190
191 static_assert(Removing <= ModeMask, "timer slot modes must fit their mask");
192
194 SchedulerTimerHandlerSlot& operator=(const SchedulerTimerHandlerSlot&) = delete;
195
196 void completeDispatch() {
197 // Only a successfully attached guard can call this. Unpublish accepts
198 // only Published with a zero count, so the mode cannot change while an
199 // admission is live.
200 __atomic_fetch_sub(&m_State, DispatchIncrement, __ATOMIC_ACQ_REL);
201 }
202
203 SchedulerTimerHandler* m_Handler;
204 size_t m_Owner;
205 size_t m_State;
206};
207
208#endif
bool beginDispatch(size_t owner, DispatchGuard &guard)
bool publish(size_t owner, SchedulerTimerHandler *handler)
bool unpublish(size_t owner, SchedulerTimerHandler *handler)
bool isPublished(size_t owner, SchedulerTimerHandler *handler) const