The Pedigree Project 0.1
hosted-unix-regressions.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/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/process/Scheduler.h"
11#include "pedigree/kernel/process/Semaphore.h"
12#include "pedigree/kernel/process/Thread.h"
13#include "pedigree/kernel/time/Time.h"
14
15#include "modules/subsys/posix/UnixFilesystem.h"
16
17namespace {
18bool check(bool condition, const char* test, const char* detail) {
19 if (condition) {
20 return true;
21 }
22
23 ERROR("HOSTED-WAIT-TEST: FAIL " << test << ": " << detail);
24 return false;
25}
26
27bool waitUntilQueued(Thread* thread) {
28 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
29 while (Time::getTicks() < deadline) {
30 Thread::WaitDebugInfo info = {};
31 uintptr_t debugAddress = 0;
32 if (thread->getWaitDebugInfo(info) && info.queue && info.queued &&
33 thread->getDebugState(debugAddress) == Thread::SemWait) {
34 return true;
35 }
37 }
38 return false;
39}
40
41struct PollWaitContext {
42 explicit PollWaitContext(Semaphore* readiness) : readiness(readiness), entered(0), woke(0) {}
43
44 Semaphore* readiness;
45 Atomic<size_t> entered;
46 Atomic<size_t> woke;
47};
48
49int waitForPollReadiness(void* parameter) {
50 PollWaitContext* context = reinterpret_cast<PollWaitContext*>(parameter);
51 context->entered += 1;
52 if (context->readiness->acquireForCompletion()) {
53 context->woke += 1;
54 }
55 return 0;
56}
57
58Thread* startPollWaiter(PollWaitContext& context, const char* name) {
59 Thread* waiter = new Thread(Scheduler::instance().getKernelProcess(), waitForPollReadiness,
60 &context, nullptr, false, true);
61 waiter->setName(String(name));
62 return waiter;
63}
64
65bool datagramPollSendWake() {
66 UnixSocket socket(String(), nullptr, nullptr, nullptr, UnixSocket::Datagram);
67 Semaphore readiness(0, false);
68 socket.addWaiter(&readiness, true, false);
69
70 PollWaitContext context(&readiness);
71 Thread* waiter = startPollWaiter(context, "hosted Unix datagram send poll");
72 const bool queued = waitUntilQueued(waiter);
73
74 char sent = 'd';
75 const bool wrote = socket.writeBytewise(0, sizeof(sent), reinterpret_cast<uintptr_t>(&sent),
76 false) == sizeof(sent);
77 const bool joined = waiter->join();
78
79 char received = 0;
80 String from;
81 const bool read = socket.recvfrom(sizeof(received), reinterpret_cast<uintptr_t>(&received), false,
82 from) == sizeof(received);
83
84 const bool passed = check(
85 context.entered == 1 && queued && wrote && joined && context.woke == 1 && read &&
86 received == sent && !readiness.tryAcquire(),
87 "unix-datagram-poll-send", "a datagram did not wake exactly the waiter enrolled on its ring");
88 if (passed) {
89 NOTICE("HOSTED-WAIT-TEST: PASS unix-datagram-poll-send");
90 }
91 return passed;
92}
93
94bool datagramPollCloseWake() {
95 UnixSocket socket(String(), nullptr, nullptr, nullptr, UnixSocket::Datagram);
96 Semaphore readiness(0, false);
97 socket.addWaiter(&readiness, true, false);
98
99 PollWaitContext context(&readiness);
100 Thread* waiter = startPollWaiter(context, "hosted Unix datagram close poll");
101 const bool queued = waitUntilQueued(waiter);
102
103 socket.unbind();
104 const bool joined = waiter->join();
105
106 Semaphore lateReadiness(0, false);
107 socket.addWaiter(&lateReadiness, true, false);
108
109 const bool passed = check(
110 context.entered == 1 && queued && joined && context.woke == 1 &&
111 socket.getState() == UnixSocket::Closed && socket.select(false, 0) &&
112 !socket.select(true, 0) && !readiness.tryAcquire() && lateReadiness.tryAcquire(),
113 "unix-datagram-poll-close", "datagram close did not wake poll with persistent EOF readiness");
114 if (passed) {
115 NOTICE("HOSTED-WAIT-TEST: PASS unix-datagram-poll-close");
116 }
117 return passed;
118}
119
120bool streamPollCloseWake() {
121 UnixSocket local(String(), nullptr, nullptr, nullptr, UnixSocket::Streaming);
122 UnixSocket peer(String(), nullptr, nullptr, nullptr, UnixSocket::Streaming);
123 const bool connected = local.bind(&peer);
124 peer.acknowledgeBind();
125
126 Semaphore readiness(0, false);
127 local.addWaiter(&readiness, true, false);
128
129 PollWaitContext context(&readiness);
130 Thread* waiter = startPollWaiter(context, "hosted Unix stream close poll");
131 const bool queued = waitUntilQueued(waiter);
132
133 peer.unbind();
134 const bool joined = waiter->join();
135
136 Semaphore lateReadiness(0, false);
137 local.addWaiter(&lateReadiness, true, false);
138
139 const bool passed = check(
140 connected && context.entered == 1 && queued && joined && context.woke == 1 &&
141 local.getState() == UnixSocket::Closed && local.select(false, 0) &&
142 !local.select(true, 0) && !readiness.tryAcquire() && lateReadiness.tryAcquire() &&
143 !lateReadiness.tryAcquire(),
144 "unix-stream-poll-close", "stream close did not wake poll with persistent EOF readiness");
145 if (passed) {
146 NOTICE("HOSTED-WAIT-TEST: PASS unix-stream-poll-close");
147 }
148 return passed;
149}
150} // namespace
151
152EXPORTED_PUBLIC bool runHostedUnixDatagramRegressions() {
153 return datagramPollSendWake() && datagramPollCloseWake() && streamPollCloseWake();
154}
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
bool getWaitDebugInfo(WaitDebugInfo &info)
Definition Thread.cc:3163
DebugState getDebugState(uintptr_t &address)
Definition Thread.h:574