8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/process/Scheduler.h"
11#include "pedigree/kernel/process/Thread.h"
12#include "pedigree/kernel/time/Time.h"
14#include "modules/system/vfs/Pipe.h"
17class EndpointLockedPipe :
public Pipe {
19 explicit EndpointLockedPipe(
const char* name, uintptr_t inode)
20 :
Pipe(
String(name), 0, 0, 0, inode, nullptr, 0, nullptr, false) {}
22 void lockEndpointState() {
26 void unlockEndpointState() {
31struct PipeWaitContext {
32 explicit PipeWaitContext(
Pipe* pipe) : pipe(pipe), entered(0), returned(0), observedReader(0) {}
46 PipeIoContext(
Pipe* pipe, Operation operation)
47 : pipe(pipe), operation(operation), entered(0), returned(0), result(1), value(
'p') {}
57int accessPipeEndpointState(
void* parameter) {
58 PipeIoContext* context =
reinterpret_cast<PipeIoContext*
>(parameter);
59 context->entered += 1;
60 if (context->operation == PipeIoContext::Read) {
62 context->pipe->readBytewise(0, 1,
reinterpret_cast<uintptr_t
>(&context->value),
false);
65 context->pipe->writeBytewise(0, 1,
reinterpret_cast<uintptr_t
>(&context->value),
false);
67 context->returned += 1;
71int waitForPipeReader(
void* parameter) {
72 PipeWaitContext* context =
reinterpret_cast<PipeWaitContext*
>(parameter);
73 context->entered += 1;
74 if (context->pipe->waitForReader(
true)) {
75 context->observedReader += 1;
77 context->returned += 1;
81Thread* startPipeWaiter(PipeWaitContext& context,
const char* name) {
83 nullptr,
false,
true);
84 thread->setName(
String(name));
89 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
90 while (value < expected && Time::getTicks() < deadline) {
93 return value == expected;
96bool waitUntilBlocked(
Thread* thread) {
97 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
98 while (Time::getTicks() < deadline) {
100 uintptr_t address = 0;
105 if (thread->
getStatus() == Thread::AwaitingJoin) {
113bool waitUntilEndpointLockBlocked(
Thread* thread) {
114 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
115 while (Time::getTicks() < deadline) {
116 uintptr_t address = 0;
120 if (thread->
getStatus() == Thread::AwaitingJoin) {
128bool runEndpointLockCase(PipeIoContext::Operation operation,
const char* name, uintptr_t inode) {
129 EndpointLockedPipe pipe(name, inode);
130 PipeIoContext context(&pipe, operation);
131 pipe.lockEndpointState();
133 &context,
nullptr,
false,
true);
134 worker->setName(
"hosted pipe endpoint snapshot");
137 waitUntilEndpointLockBlocked(
worker) && context.entered == 1 && context.returned == 0;
138 pipe.unlockEndpointState();
139 const bool joined =
worker->join();
140 return blocked && joined && context.returned == 1 && context.result == 0;
143bool endpointCountsUseFileMutex() {
144 return runEndpointLockCase(PipeIoContext::Read,
"hosted-locked-reader-fifo", 3) &&
145 runEndpointLockCase(PipeIoContext::Write,
"hosted-locked-writer-fifo", 4);
148bool staleReaderDoesNotSatisfyOpen() {
149 Pipe pipe(
String(
"hosted-stale-reader-fifo"), 0, 0, 0, 1,
nullptr, 0,
nullptr,
false);
152 pipe.increaseRefCount(
false);
153 pipe.decreaseRefCount(
false);
155 PipeWaitContext context(&pipe);
156 Thread*
waiter = startPipeWaiter(context,
"hosted stale FIFO reader waiter");
157 const bool blocked = waitUntilBlocked(
waiter) && context.returned == 0;
159 pipe.increaseRefCount(
false);
160 const bool returned = waitForCount(context.returned, 1);
161 const bool joined =
waiter->join();
162 pipe.decreaseRefCount(
false);
164 return blocked && returned && joined && context.observedReader == 1;
167bool oneReaderWakesEveryWriter() {
168 Pipe pipe(
String(
"hosted-broadcast-reader-fifo"), 0, 0, 0, 2,
nullptr, 0,
nullptr,
false);
169 PipeWaitContext firstContext(&pipe);
170 PipeWaitContext secondContext(&pipe);
171 Thread* first = startPipeWaiter(firstContext,
"hosted FIFO writer waiter one");
172 Thread* second = startPipeWaiter(secondContext,
"hosted FIFO writer waiter two");
174 const bool bothBlocked = waitUntilBlocked(first) && waitUntilBlocked(second) &&
175 firstContext.returned == 0 && secondContext.returned == 0;
177 pipe.increaseRefCount(
false);
178 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
179 while ((firstContext.returned != 1 || secondContext.returned != 1) &&
180 Time::getTicks() < deadline) {
183 const bool bothReturned = firstContext.returned == 1 && secondContext.returned == 1;
188 pipe.increaseRefCount(
false);
190 const bool firstJoined = first->
join();
191 const bool secondJoined = second->
join();
192 pipe.decreaseRefCount(
false);
194 pipe.decreaseRefCount(
false);
197 return bothBlocked && bothReturned && firstJoined && secondJoined &&
198 firstContext.observedReader == 1 && secondContext.observedReader == 1;
202bool runHostedPipeRegressions() {
203 const bool passed = staleReaderDoesNotSatisfyOpen() && oneReaderWakesEveryWriter() &&
204 endpointCountsUseFileMutex();
206 NOTICE(
"HOSTED-WAIT-TEST: PASS fifo-reader-predicate");
209 "HOSTED-WAIT-TEST: FAIL fifo-reader-predicate: FIFO open did not "
210 "use one locked reader predicate");
static Scheduler & instance()
bool getWaitDebugInfo(WaitDebugInfo &info)
DebugState getDebugState(uintptr_t &address)