The Pedigree Project 0.1
eventfd-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, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 */
8
9#include "pedigree/kernel/Atomic.h"
10#include "pedigree/kernel/Log.h"
11#include "pedigree/kernel/errors.h"
12#include "pedigree/kernel/process/Process.h"
13#include "pedigree/kernel/process/Scheduler.h"
14#include "pedigree/kernel/process/Thread.h"
15#include "pedigree/kernel/processor/Processor.h"
16
17#include <fcntl.h>
18#include <stdint.h>
19
20#include "modules/subsys/posix/FileDescriptor.h"
21#include "modules/subsys/posix/PosixSubsystem.h"
22#include "modules/subsys/posix/eventfd-syscalls.h"
23#include "modules/subsys/posix/file-syscalls.h"
24#include "modules/subsys/posix/poll-syscalls.h"
25#include <sys/uio.h>
26
27namespace {
28constexpr size_t HostedAttempts = 10000;
29
30struct EventFdCloseContext {
31 explicit EventFdCloseContext(int fd)
32 : fd(fd), entered(0), returned(0), result(-2), error(0), value(0) {}
33
34 int fd;
35 Atomic<size_t> entered;
36 Atomic<size_t> returned;
37 int result;
38 int error;
39 uint64_t value;
40};
41
42int blockOnEventFd(void* parameter) {
43 EventFdCloseContext* context = reinterpret_cast<EventFdCloseContext*>(parameter);
44 Thread* thread = Processor::information().getCurrentThread();
45 context->entered += 1;
46 thread->setErrno(0);
47 context->result =
48 posix_read(context->fd, reinterpret_cast<char*>(&context->value), sizeof(context->value));
49 context->error = thread->getErrno();
50 context->returned += 1;
51 return 0;
52}
53
54struct EventFdRegressionContext {
55 EventFdRegressionContext() : passed(false), returned(0) {}
56
57 bool passed;
58 Atomic<size_t> returned;
59};
60
61int exerciseEventFd(void* parameter) {
62 EventFdRegressionContext* context = reinterpret_cast<EventFdRegressionContext*>(parameter);
63 Thread* thread = Processor::information().getCurrentThread();
64 bool passed = true;
65
66 thread->setErrno(0);
67 const int invalidFlags = posix_eventfd2(0, 0x40000000);
68 passed &= invalidFlags == -1 && thread->getErrno() == Error::InvalidArgument;
69
70 const int fd = posix_eventfd2(0, LinuxEventFd::NonBlock | LinuxEventFd::CloseOnExec);
71 DescriptorLease descriptor;
72 passed &= fd >= 0 && acquireDescriptor(fd, descriptor) && descriptor->getFlags() == FD_CLOEXEC &&
73 descriptor->getStatusFlags() == (O_RDWR | O_NONBLOCK) && descriptor->getEventFdImpl() &&
74 descriptor->getEventFdImpl()->queryReady() == ReadyWrite;
75 descriptor.reset();
76
77 char shortBuffer[sizeof(uint64_t) - 1] = {};
78 uint64_t value = 0;
79 thread->setErrno(0);
80 const int shortRead = posix_read(fd, shortBuffer, sizeof(shortBuffer));
81 passed &= shortRead == -1 && thread->getErrno() == Error::InvalidArgument;
82 thread->setErrno(0);
83 const int emptyRead = posix_read(fd, reinterpret_cast<char*>(&value), sizeof(value));
84 passed &= emptyRead == -1 && thread->getErrno() == Error::NoMoreProcesses;
85 thread->setErrno(0);
86 const int shortWrite = posix_write(fd, shortBuffer, sizeof(shortBuffer), false);
87 passed &= shortWrite == -1 && thread->getErrno() == Error::InvalidArgument;
88 value = ~static_cast<uint64_t>(0);
89 thread->setErrno(0);
90 const int invalidWrite = posix_write(fd, reinterpret_cast<char*>(&value), sizeof(value), false);
91 passed &= invalidWrite == -1 && thread->getErrno() == Error::InvalidArgument;
92
93 value = 2;
94 const int initialWrite = posix_write(fd, reinterpret_cast<char*>(&value), sizeof(value), false);
95 struct pollfd pollState = {fd, POLLIN | POLLOUT, 0};
96 const int pollResult = posix_poll_safe(&pollState, 1, 0);
97 uint64_t longRead[2] = {};
98 const int accumulatedRead = posix_read(fd, reinterpret_cast<char*>(longRead), sizeof(longRead));
99 passed &= initialWrite == 8 && pollResult == 1 &&
100 (pollState.revents & (POLLIN | POLLOUT)) == (POLLIN | POLLOUT) &&
101 accumulatedRead == 8 && longRead[0] == 2 && longRead[1] == 0;
102
103 uint64_t longWrite[2] = {3, 5};
104 thread->setErrno(0);
105 const int longWriteResult =
106 posix_write(fd, reinterpret_cast<char*>(longWrite), sizeof(longWrite), false);
107 passed &= longWriteResult == -1 && thread->getErrno() == Error::InvalidArgument;
108
109 uint64_t vectorWriteValue = 7;
110 struct iovec writeVectors[2] = {
111 {&vectorWriteValue, 3},
112 {reinterpret_cast<uint8_t*>(&vectorWriteValue) + 3, 5},
113 };
114 thread->setErrno(0);
115 const int splitVectorWrite = posix_writev(fd, writeVectors, 2);
116 const int splitVectorError = thread->getErrno();
117
118 uint64_t vectorWriteValues[2] = {7, 9};
119 struct iovec completeWriteVectors[2] = {
120 {&vectorWriteValues[0], sizeof(vectorWriteValues[0])},
121 {&vectorWriteValues[1], sizeof(vectorWriteValues[1])},
122 };
123 const int vectorWrite = posix_writev(fd, completeWriteVectors, 2);
124 uint64_t vectorReadValue = 0;
125 struct iovec readVectors[2] = {
126 {&vectorReadValue, 2},
127 {reinterpret_cast<uint8_t*>(&vectorReadValue) + 2, 6},
128 };
129 const int vectorRead = posix_readv(fd, readVectors, 2);
130 passed &= splitVectorWrite == -1 && splitVectorError == Error::InvalidArgument &&
131 vectorWrite == 16 && vectorRead == 8 && vectorReadValue == 16;
132
133 const int alias = posix_dup(fd);
134 const int originalClose = posix_close(fd);
135 value = 4;
136 const int aliasWrite = posix_write(alias, reinterpret_cast<char*>(&value), sizeof(value), false);
137 value = 0;
138 const int aliasRead = posix_read(alias, reinterpret_cast<char*>(&value), sizeof(value));
139 passed &= alias >= 0 && originalClose == 0 && aliasWrite == 8 && aliasRead == 8 && value == 4;
140
141 value = ~static_cast<uint64_t>(0) - 1;
142 const int maximumWrite =
143 posix_write(alias, reinterpret_cast<char*>(&value), sizeof(value), false);
144 pollState = {alias, POLLIN | POLLOUT, 0};
145 const int maximumPoll = posix_poll_safe(&pollState, 1, 0);
146 value = 1;
147 thread->setErrno(0);
148 const int overflowWrite =
149 posix_write(alias, reinterpret_cast<char*>(&value), sizeof(value), false);
150 const int overflowError = thread->getErrno();
151 value = 0;
152 const int maximumRead = posix_read(alias, reinterpret_cast<char*>(&value), sizeof(value));
153 passed &= maximumWrite == 8 && maximumPoll == 1 && (pollState.revents & POLLIN) &&
154 !(pollState.revents & POLLOUT) && overflowWrite == -1 &&
155 overflowError == Error::NoMoreProcesses && maximumRead == 8 &&
156 value == ~static_cast<uint64_t>(0) - 1 && posix_close(alias) == 0;
157
158 const int semaphoreFd = posix_eventfd2(2, LinuxEventFd::Semaphore | LinuxEventFd::NonBlock);
159 uint64_t firstSemaphore = 0;
160 uint64_t secondSemaphore = 0;
161 const int firstSemaphoreRead =
162 posix_read(semaphoreFd, reinterpret_cast<char*>(&firstSemaphore), sizeof(firstSemaphore));
163 const int secondSemaphoreRead =
164 posix_read(semaphoreFd, reinterpret_cast<char*>(&secondSemaphore), sizeof(secondSemaphore));
165 thread->setErrno(0);
166 const int emptySemaphoreRead =
167 posix_read(semaphoreFd, reinterpret_cast<char*>(&value), sizeof(value));
168 const int emptySemaphoreError = thread->getErrno();
169 passed &= semaphoreFd >= 0 && firstSemaphoreRead == 8 && secondSemaphoreRead == 8 &&
170 firstSemaphore == 1 && secondSemaphore == 1 && emptySemaphoreRead == -1 &&
171 emptySemaphoreError == Error::NoMoreProcesses && posix_close(semaphoreFd) == 0;
172
173 const int blockingFd = posix_eventfd(0);
174 DescriptorLease blockingDescriptor;
175 SharedPointer<EventFd> retainedEventFd;
176 if (blockingFd >= 0 && acquireDescriptor(blockingFd, blockingDescriptor)) {
177 retainedEventFd = blockingDescriptor->getEventFdImpl();
178 }
179 blockingDescriptor.reset();
180 EventFdCloseContext closeContext(blockingFd);
181 Thread* reader =
182 new Thread(thread->getParent(), blockOnEventFd, &closeContext, nullptr, false, true, true);
183 reader->setName("hosted eventfd close waiter");
184 const bool readerStarted = reader->start();
185 bool readerBlocked = false;
186 for (size_t attempt = 0; attempt < HostedAttempts && readerStarted; ++attempt) {
187 Thread::WaitDebugInfo info = {};
188 if (closeContext.entered && reader->getWaitDebugInfo(info) && info.queue && info.queued &&
189 reader->getStatus() == Thread::Sleeping) {
190 readerBlocked = true;
191 break;
192 }
194 }
195 const int blockingClose = readerBlocked ? posix_close(blockingFd) : -1;
196 for (size_t attempt = 0; attempt < 256 && !closeContext.returned; ++attempt) {
198 }
199 const bool remainedBlocked = !closeContext.returned;
200 const int retainedWrite =
201 retainedEventFd && remainedBlocked ? retainedEventFd->writeValue(1, false) : -1;
202 const bool readerJoined = readerStarted && reader->joinForCompletion();
203 if (!readerStarted) {
204 delete reader;
205 }
206 passed &= blockingFd >= 0 && readerStarted && readerBlocked && blockingClose == 0 &&
207 remainedBlocked && retainedWrite == 8 && readerJoined && closeContext.returned == 1 &&
208 closeContext.result == 8 && closeContext.error == 0 && closeContext.value == 1;
209
210 context->passed = passed;
211 context->returned += 1;
212 return passed ? 0 : 1;
213}
214} // namespace
215
216bool runHostedEventFdRegressions(Process* kernelProcess) {
217 Process* process = new Process(kernelProcess);
218 process->setSubsystem(new PosixSubsystem);
219 EventFdRegressionContext context;
220 Thread* worker = new Thread(process, exerciseEventFd, &context, nullptr, false, true, true);
221 worker->setName("hosted eventfd regression worker");
222 const bool started = worker->start();
223 const bool joined = started && worker->joinForCompletion();
224 if (!started) {
225 delete worker;
226 }
227
228 const bool passed = started && joined && context.returned == 1 && context.passed;
229 delete process;
230 if (!passed) {
231 ERROR(
232 "HOSTED-SYSCALL-TEST: FAIL eventfd-counter-readiness-lifetime: "
233 "flags, counter, vector I/O, polling, alias, or in-flight close semantics failed");
234 return false;
235 }
236
237 NOTICE("HOSTED-SYSCALL-TEST: PASS eventfd-counter-readiness-lifetime");
238 return true;
239}
ReadyMask queryReady()
SharedPointer< EventFd > getEventFdImpl() const
int getFlags() const
Get current descriptor flags.
int getStatusFlags() const
Get current status flags.
static ProcessorInformation & information()
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:226
void setErrno(size_t err)
Definition Thread.h:478
size_t getErrno()
Definition Thread.h:473
Process * getParent() const
Definition Thread.h:338
Definition waits.c:9