The Pedigree Project 0.1
posix-timer-syscalls.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/machine/Machine.h"
10#include "pedigree/kernel/machine/Timer.h"
11#include "pedigree/kernel/machine/TimerHandler.h"
12#include "pedigree/kernel/panic.h"
13#include "pedigree/kernel/process/Mutex.h"
14#include "pedigree/kernel/process/Process.h"
15#include "pedigree/kernel/process/Scheduler.h"
16#include "pedigree/kernel/process/TerminationDeferral.h"
17#include "pedigree/kernel/process/Thread.h"
18#include "pedigree/kernel/processor/Processor.h"
19#include "pedigree/kernel/processor/ProcessorInformation.h"
20#include "pedigree/kernel/syscallError.h"
21#include "pedigree/kernel/utilities/SharedPointer.h"
22
23#include "PosixSubsystem.h"
24#include "posix-timer-state.h"
25#include "posix-timer-syscalls.h"
26#include "queued-signal.h"
27
28namespace {
29constexpr size_t MaximumTimers = 256;
30constexpr size_t MaximumProcessTimers = 64;
31constexpr unsigned MaximumGeneration = 0x7fffff;
32constexpr int ClockRealtime = 0, ClockMonotonic = 1, Absolute = 1;
33constexpr int Signal = 0, None = 1, ThreadId = 4;
34
35struct EventPrefix {
36 uint64_t value;
37 int32_t signal;
38 int32_t notification;
39};
40static_assert(sizeof(EventPrefix) == 16, "Linux sigevent prefix ABI");
41
42struct Entry {
43 Process* owner = nullptr;
44 Thread* target = nullptr;
45 size_t targetId = 0;
46 int id = 0;
47 int clock = ClockRealtime;
48 EventPrefix event = {};
51};
52
53class Registry : public TimerHandler {
54 public:
55 ~Registry() override {
56 if (source && !source->unregisterHandler(this))
57 panic("POSIX timer callback could not be drained");
58 }
59
60 Entry* find(int id) {
61 if (id < 0)
62 return nullptr;
63 Entry& entry = entries[static_cast<unsigned>(id) % MaximumTimers];
64 return entry.owner == Processor::information().getCurrentThread()->getParent() && entry.id == id
65 ? &entry
66 : nullptr;
67 }
68
69 void advance(Entry& entry, Time::Timestamp now) {
70 const uint64_t expired = PosixTimerState::advance(entry.state, now);
71 if (entry.event.notification == None || !entry.notification->addExpirations(expired))
72 return;
73
76 if (!Scheduler::instance().acquireProcess(process, entry.owner) ||
77 (entry.target && (!process->acquireThread(target, entry.target) ||
78 target->getTaskId() != entry.targetId)) ||
79 posix_signal_queue_timer(process.get(), target.get(), entry.event.signal, entry.event.value,
80 entry.notification) < 0)
81 entry.notification->queueFailed();
82 }
83
84 void remove(Entry& entry) {
85 posix_signal_cancel_timer(entry.owner, entry.notification);
86 entry = Entry();
87 }
88
89 void armNextLocked() {
90 if (!source || !source->supportsDeadlines())
91 return;
92 const Time::Timestamp monotonic = Time::getTicks();
93 const Time::Timestamp realtime = Time::getTimeNanoseconds();
94 Time::Timestamp next = 0;
95 for (const Entry& entry : entries) {
96 if (!entry.owner)
97 continue;
98 const Time::Timestamp deadline =
99 PosixTimerState::monotonicDeadline(entry.state, realtime, monotonic);
100 if (deadline && (!next || deadline < next))
101 next = deadline;
102 }
103 if (!source->armHandler(this, next))
104 panic("POSIX timer registry could not arm its callback");
105 }
106
107 void timer(uint64_t) override {
108 // A deadline callback must wait for mutations: there is no periodic tick
109 // to retry it if the registry is busy.
111 return;
112 TerminationDeferral lifetime;
113 if (source && source->supportsDeadlines())
114 lock.acquire();
115 else if (!lock.tryAcquire())
116 return;
117 const Time::Timestamp monotonic = Time::getTicks();
118 const Time::Timestamp realtime = Time::getTimeNanoseconds();
119 for (Entry& entry : entries)
120 if (entry.owner)
121 advance(entry, entry.state.realtime ? realtime : monotonic);
122 armNextLocked();
123 lock.release();
124 }
125
126 Mutex lock;
127 Timer* source = nullptr;
128 Entry entries[MaximumTimers];
129 unsigned generations[MaximumTimers] = {};
130};
131
132Registry timers;
133} // namespace
134
135int posix_timer_create(int clock, const void* event, int* timerId) {
136 if (clock != ClockRealtime && clock != ClockMonotonic) {
137 SYSCALL_ERROR(InvalidArgument);
138 return -1;
139 }
140 EventPrefix requested = {0, 14, Signal};
141 int32_t targetId = 0;
142 if (event) {
143 // musl uses a compact ksigevent, so the unused public sigevent tail is
144 // neither accessible nor meaningful to this syscall.
145 if (!PosixSubsystem::copyFromUser(&requested, event, sizeof(requested))) {
146 SYSCALL_ERROR(BadAddress);
147 return -1;
148 }
149 if (requested.notification == ThreadId &&
151 reinterpret_cast<const uint8_t*>(event) + sizeof(requested),
152 sizeof(targetId))) {
153 SYSCALL_ERROR(BadAddress);
154 return -1;
155 }
156 }
157 if ((requested.notification != Signal && requested.notification != None &&
158 requested.notification != ThreadId) ||
159 (requested.notification != None && (requested.signal < 1 || requested.signal > 64))) {
160 SYSCALL_ERROR(InvalidArgument);
161 return -1;
162 }
163
164 Process* process = Processor::information().getCurrentThread()->getParent();
165 LockGuard<Mutex> guard(timers.lock);
167 if (requested.notification == ThreadId &&
168 (targetId <= 0 ||
169 !process->acquireThreadByUserspaceId(target, targetId, process->pidNamespace().get()) ||
170 target->getUnwindState() != Thread::Continue || !target->acceptingEvents())) {
171 SYSCALL_ERROR(InvalidArgument);
172 return -1;
173 }
174
175 size_t freeSlot = MaximumTimers, owned = 0;
176 for (size_t i = 0; i < MaximumTimers; ++i) {
177 if (!timers.entries[i].owner && freeSlot == MaximumTimers)
178 freeSlot = i;
179 if (timers.entries[i].owner == process)
180 ++owned;
181 }
182 if (freeSlot == MaximumTimers || owned == MaximumProcessTimers) {
183 SYSCALL_ERROR(NoMoreProcesses);
184 return -1;
185 }
186 if (!timers.source) {
187 Timer* source = Machine::instance().getTimer();
188 if (!source || !source->registerHandler(&timers)) {
189 SYSCALL_ERROR(NoMoreProcesses);
190 return -1;
191 }
192 timers.source = source;
193 }
194
196 if (!posix_signal_reserve_timer(process, notification))
197 return -1;
198 unsigned generation = (timers.generations[freeSlot] + 1) & MaximumGeneration;
199 if (!generation)
200 generation = 1;
201 const int id = generation * MaximumTimers + freeSlot;
202 timers.generations[freeSlot] = generation;
203 notification->timerId = id;
204 if (!PosixSubsystem::copyToUser(timerId, &id, sizeof(id))) {
205 posix_signal_cancel_timer(process, notification);
206 SYSCALL_ERROR(BadAddress);
207 return -1;
208 }
209
210 Entry& entry = timers.entries[freeSlot];
211 entry.id = id;
212 entry.clock = clock;
213 entry.event = requested;
214 if (!event)
215 entry.event.value = id;
216 entry.target = target.get();
217 entry.targetId = target ? target->getTaskId() : 0;
218 entry.notification = notification;
219 entry.owner = process;
220 return 0;
221}
222
223int posix_timer_settime(int timerId, int flags, const void* setting, void* previous) {
224 if ((flags & ~Absolute) || !setting) {
225 SYSCALL_ERROR(InvalidArgument);
226 return -1;
227 }
228 PosixTimerState::Setting requested;
229 if (!PosixSubsystem::copyFromUser(&requested, setting, sizeof(requested))) {
230 SYSCALL_ERROR(BadAddress);
231 return -1;
232 }
233 Time::Timestamp value, interval;
234 if (!PosixTimerState::decode(requested.value, value) ||
235 !PosixTimerState::decode(requested.interval, interval)) {
236 SYSCALL_ERROR(InvalidArgument);
237 return -1;
238 }
239
240 LockGuard<Mutex> guard(timers.lock);
241 Entry* entry = timers.find(timerId);
242 if (!entry) {
243 SYSCALL_ERROR(InvalidArgument);
244 return -1;
245 }
246 const Time::Timestamp oldNow =
247 entry->state.realtime ? Time::getTimeNanoseconds() : Time::getTicks();
248 timers.advance(*entry, oldNow);
249 if (previous) {
250 const PosixTimerState::Setting old = PosixTimerState::snapshot(entry->state, oldNow);
251 if (!PosixSubsystem::copyToUser(previous, &old, sizeof(old))) {
252 timers.armNextLocked();
253 SYSCALL_ERROR(BadAddress);
254 return -1;
255 }
256 }
257
258 posix_signal_reset_timer(entry->owner, entry->notification);
259 entry->state.realtime = (flags & Absolute) && entry->clock == ClockRealtime;
260 const Time::Timestamp now = entry->state.realtime ? Time::getTimeNanoseconds() : Time::getTicks();
261 entry->state.armed = value != 0;
262 entry->state.interval = value ? interval : 0;
263 entry->state.deadline = flags & Absolute ? value : PosixTimerState::add(now, value);
264 timers.advance(*entry, now);
265 timers.armNextLocked();
266 return 0;
267}
268
269int posix_timer_gettime(int timerId, void* setting) {
270 LockGuard<Mutex> guard(timers.lock);
271 Entry* entry = timers.find(timerId);
272 if (!entry) {
273 SYSCALL_ERROR(InvalidArgument);
274 return -1;
275 }
276 const Time::Timestamp now = entry->state.realtime ? Time::getTimeNanoseconds() : Time::getTicks();
277 timers.advance(*entry, now);
278 timers.armNextLocked();
279 const PosixTimerState::Setting result = PosixTimerState::snapshot(entry->state, now);
280 if (!PosixSubsystem::copyToUser(setting, &result, sizeof(result))) {
281 SYSCALL_ERROR(BadAddress);
282 return -1;
283 }
284 return 0;
285}
286
287int posix_timer_getoverrun(int timerId) {
288 LockGuard<Mutex> guard(timers.lock);
289 Entry* entry = timers.find(timerId);
290 if (!entry) {
291 SYSCALL_ERROR(InvalidArgument);
292 return -1;
293 }
294 return entry->notification->getDeliveredOverrun();
295}
296
297int posix_timer_delete(int timerId) {
298 LockGuard<Mutex> guard(timers.lock);
299 Entry* entry = timers.find(timerId);
300 if (!entry) {
301 SYSCALL_ERROR(InvalidArgument);
302 return -1;
303 }
304 timers.remove(*entry);
305 timers.armNextLocked();
306 return 0;
307}
308
309void posix_timer_process_exit(Process* process) {
310 if (!process)
311 return;
312 LockGuard<Mutex> guard(timers.lock);
313 for (Entry& entry : timers.entries)
314 if (entry.owner == process)
315 timers.remove(entry);
316 timers.armNextLocked();
317}
318
319void posix_timer_thread_exit(Thread* thread) {
320 if (!thread)
321 return;
322 LockGuard<Mutex> guard(timers.lock);
323 for (Entry& entry : timers.entries)
324 if (entry.owner && entry.target == thread)
325 timers.remove(entry);
326 timers.armNextLocked();
327}
328
329void posix_timer_clock_changed() {
330 LockGuard<Mutex> guard(timers.lock);
331 timers.armNextLocked();
332}
virtual Timer * getTimer()=0
Definition Mutex.h:56
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
Process * getParent()
Definition Process.h:620
MUST_USE_RESULT bool acquireThread(ThreadLease &lease, size_t n)
Definition Process.cc:1385
static ProcessorInformation & information()
static bool inDeviceHardIrq()
Definition Processor.h:581
static Scheduler & instance()
Definition Scheduler.h:96
T * get() const
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:517
UnwindType getUnwindState()
Definition Thread.h:535
virtual void timer(uint64_t delta)=0
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118