The Pedigree Project 0.1
pending-signal.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Scheduler.h"
4#include "pedigree/kernel/process/SignalEvent.h"
5#include "pedigree/kernel/process/Thread.h"
6#include "pedigree/kernel/utilities/lib.h"
7
8#include "PosixSubsystem.h"
9#include "queued-signal.h"
10
11void PendingSignalContext::attach(Process* process) {
12 LockGuard<Mutex> guard(lock);
13 if (!m_Closed && (!m_Process || m_Process == process))
14 m_Process = process;
15}
16void PendingSignalContext::close() {
17 if (__atomic_load_n(&m_Closed, __ATOMIC_ACQUIRE))
18 return;
19 {
20 LockGuard<Mutex> guard(lock);
21 __atomic_store_n(&m_Closed, true, __ATOMIC_RELEASE);
22 m_Process = nullptr;
23 for (auto& binding : m_Bindings)
24 binding->alive = false;
25 m_Bindings.clear();
26 recordChange();
27 }
29}
30void PendingSignalContext::retireThread(Thread* thread) {
31 {
32 LockGuard<Mutex> guard(lock);
33 for (auto it = m_Bindings.begin(); it != m_Bindings.end();) {
34 if ((*it)->thread == thread) {
35 (*it)->alive = false;
36 it = m_Bindings.erase(it);
37 } else
38 ++it;
39 }
40 recordChange();
41 }
42 publish();
43}
44SharedPointer<PendingSignalBinding> PendingSignalContext::bind(Thread* thread) {
45 LockGuard<Mutex> guard(lock);
46 if (m_Closed || !thread || thread->getParent() != m_Process || !thread->acceptingEvents())
48 for (auto& binding : m_Bindings)
49 if (binding->thread == thread && binding->alive)
50 return binding;
52 binding->thread = thread;
53 m_Bindings.pushBack(binding);
54 return binding;
55}
56ReadyMask PendingSignalContext::query(const SharedPointer<PendingSignalBinding>& binding,
57 uint64_t mask, ReadinessGenerations* generations) {
58 LockGuard<Mutex> guard(lock);
59 if (m_Closed || !binding || !binding->alive)
60 return ReadyInvalid | ReadyHangup;
63 if (!Scheduler::instance().acquireProcess(process, m_Process) ||
64 !process->acquireThread(thread, binding->thread) || !thread->acceptingEvents())
65 return ReadyInvalid | ReadyHangup;
66 if (generations) {
67 for (size_t i = 0; i < 64; ++i)
68 if (mask & (uint64_t(1) << i))
69 generations->read += m_ProcessGenerations[i] + binding->generations[i];
70 }
71 return posix_matching_pending(thread.get(), mask) ? ReadyRead : ReadyNone;
72}
73void PendingSignalContext::recordChange(size_t signal, Thread* target, bool processDirected) {
74 if (signal && signal <= 64) {
75 if (processDirected)
76 ++m_ProcessGenerations[signal - 1];
77 else
78 for (auto& binding : m_Bindings)
79 if (binding->alive && binding->thread == target)
80 ++binding->generations[signal - 1];
81 }
82 __atomic_add_fetch(&m_Version, uint64_t(1), __ATOMIC_RELEASE);
83 changed.broadcast();
84}
85void PendingSignalContext::publish() {
86 notifyReadiness(ReadyRead | ReadyHangup);
87}
88void PendingSignalContext::wake() {
89 LockGuard<Mutex> guard(lock);
90 changed.broadcast();
91}
92
93bool posix_matching_pending(Thread* caller, uint64_t mask) {
94 if (caller->pendingSignalMask() & mask)
95 return true;
96 Process* process = caller->getParent();
97 for (size_t i = process->getNumThreads(); i > 0; --i) {
99 if (process->acquireThread(thread, i - 1) && thread.get() != caller &&
100 (thread->pendingSignalMask(true) & mask))
101 return true;
102 }
103 return false;
104}
105
106PendingSignalReservation::~PendingSignalReservation() {
107 rollback();
108}
109bool PendingSignalReservation::reserve(Thread* caller, uint64_t mask) {
110 Process* process = caller->getParent();
111 while (true) {
112 Process::ThreadLease selected;
113 uint64_t available = 0, firstSequence = ~uint64_t(0);
114 for (size_t i = process->getNumThreads(); i > 0; --i) {
116 if (!process->acquireThread(thread, i - 1))
117 continue;
118 const uint64_t candidate = thread->pendingSignalMask(thread.get() != caller) & mask;
119 const uint64_t sequence =
120 candidate
121 ? thread->pendingSignalOrder(__builtin_ctzll(candidate) + 1, thread.get() != caller)
122 : ~uint64_t(0);
123 if (candidate && (!available || __builtin_ctzll(candidate) < __builtin_ctzll(available) ||
124 (__builtin_ctzll(candidate) == __builtin_ctzll(available) &&
125 sequence < firstSequence))) {
126 available = candidate & (~candidate + 1);
127 firstSequence = sequence;
128 selected = pedigree_std::move(thread);
129 }
130 }
131 if (!selected)
132 return false;
133 Event::Delivery delivery =
134 selected->reservePendingSignal(available, selected.get() != caller, firstSequence);
135 if (!delivery)
136 continue;
137 m_Caller = caller;
138 m_Selected = pedigree_std::move(selected);
139 m_Delivery = pedigree_std::move(delivery);
140 return true;
141 }
142}
143PendingSignalRecord PendingSignalReservation::record() const {
144 auto* signal = static_cast<SignalEvent*>(m_Delivery.get());
145 PendingSignalRecord result;
146 result.number = signal->getNumber();
147 result.code = signal->getSignalCode();
148 result.pid = signal->getSenderProcess();
149 result.uid = signal->getSenderUser();
150 result.value = signal->getSignalValue();
151 signal->timerInfo(result.timerId, result.overrun);
152 result.status = signal->childStatus();
153 result.userTime = signal->childUserTime();
154 result.systemTime = signal->childSystemTime();
155 return result;
156}
157void PendingSignalReservation::commit(int32_t overrun) {
158 if (!m_Delivery)
159 return;
160 static_cast<SignalEvent*>(m_Delivery.get())->completeSignalDelivery(overrun);
161 m_Delivery.reset();
162 static_cast<PosixSubsystem*>(m_Caller->getParent()->getSubsystem())
163 ->pendingSignalContext()
164 ->recordChange();
165 m_Selected.reset();
166}
167void PendingSignalReservation::rollback() {
168 if (!m_Delivery)
169 return;
170 auto* signal = static_cast<SignalEvent*>(m_Delivery.get());
171 if (!m_Selected->restorePendingSignal(m_Delivery) && signal->isProcessDirected())
172 m_Caller->sendEvent(signal);
173 m_Delivery.reset();
174 static_cast<PosixSubsystem*>(m_Caller->getParent()->getSubsystem())
175 ->pendingSignalContext()
176 ->recordChange();
177 m_Selected.reset();
178}
179
180void posix_signal_record_siginfo(const PendingSignalRecord& signal, LinuxQueuedSiginfo& info) {
181 ByteSet(&info, 0, sizeof(info));
182 auto put32 = [&](size_t offset, int32_t value) { MemoryCopy(info.bytes + offset, &value, 4); };
183 auto put64 = [&](size_t offset, uint64_t value) { MemoryCopy(info.bytes + offset, &value, 8); };
184 put32(0, signal.number);
185 put32(8, signal.code);
186 put32(16, signal.pid);
187 put32(20, signal.uid);
188 put64(24, signal.value);
189 if (signal.code == -2) {
190 put32(16, signal.timerId);
191 put32(20, signal.overrun);
192 } else if (signal.number == 17 && signal.code > 0 && signal.code <= 6) {
193 put32(24, signal.status);
194 put64(32, signal.userTime);
195 put64(40, signal.systemTime);
196 }
197}
void reset()
Definition Event.cc:159
Iterator begin()
Definition List.h:122
Iterator end()
Definition List.h:132
size_t getNumThreads()
Definition Process.cc:1243
MUST_USE_RESULT bool acquireThread(ThreadLease &lease, size_t n)
Definition Process.cc:1248
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
Definition Readiness.cc:208
static Scheduler & instance()
Definition Scheduler.h:96
Process * getParent() const
Definition Thread.h:338
bool sendEvent(Event *pEvent)
Definition Thread.cc:1158
Iterator erase(Iterator &Iter)
Definition List.h:352
void clear()
Definition List.h:399
void pushBack(const T &value)
Definition List.h:216
Definition tracee.c:26