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"
13#include "pedigree/kernel/utilities/Buffer.h"
16enum class BufferOperation {
24struct BufferWaitContext {
25 BufferWaitContext(
Buffer<uint8_t>* buffer, BufferOperation operation, uint8_t value)
26 : buffer(buffer), operation(operation), entered(0), returned(0), result(1), value(value) {}
29 BufferOperation operation;
36int waitOnBuffer(
void* parameter) {
37 BufferWaitContext* context =
reinterpret_cast<BufferWaitContext*
>(parameter);
38 context->entered += 1;
39 switch (context->operation) {
40 case BufferOperation::Read:
41 context->result = context->buffer->read(&context->value, 1,
true);
43 case BufferOperation::Write:
44 context->result = context->buffer->write(&context->value, 1,
true);
46 case BufferOperation::WriteAvailable:
47 context->result = context->buffer->writeAvailable(&context->value, 1);
49 case BufferOperation::CanRead:
50 context->result = context->buffer->canRead(
true) ? 1 : 0;
52 case BufferOperation::CanWrite:
53 context->result = context->buffer->canWrite(
true) ? 1 : 0;
56 context->returned += 1;
60bool waitUntilBlocked(
Thread* thread, BufferWaitContext& context,
62 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
63 while (Time::getTicks() < deadline) {
65 uintptr_t address = 0;
66 if (context.entered == 1 && context.returned == 0 && thread->
getWaitDebugInfo(wait) &&
70 if (thread->
getStatus() == Thread::AwaitingJoin) {
78bool waitUntilReturned(BufferWaitContext& first, BufferWaitContext& second) {
79 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
80 while ((first.returned == 0 || second.returned == 0) && Time::getTicks() < deadline) {
83 return first.returned == 1 && second.returned == 1;
86bool availableWriteWaitsForLockOwnership() {
88 BufferWaitContext context(&buffer, BufferOperation::WriteAvailable, 0x92);
90 nullptr,
false,
true,
true);
91 writer->setName(
"hosted Buffer available-write contention");
93 buffer.acquireHostedOperationLock();
94 const bool started = writer->
start();
95 const bool blocked = started && waitUntilBlocked(writer, context, Thread::SemWait);
96 buffer.releaseHostedOperationLock();
102 uint8_t observed = 0;
103 const bool delivered = buffer.read(&observed, 1,
false) == 1 && observed == context.value;
104 return started && blocked && joined && context.returned == 1 && context.result == 1 &&
105 delivered && buffer.getHostedActiveOperationCount() == 0;
108bool blockedReaderReturnsWhenReadsAreDisabled() {
110 BufferWaitContext firstContext(&buffer, BufferOperation::Read, 0);
111 BufferWaitContext secondContext(&buffer, BufferOperation::Read, 0);
113 nullptr,
false,
true,
true);
115 &secondContext,
nullptr,
false,
true,
true);
116 first->setName(
"hosted disabled-buffer reader one");
117 second->setName(
"hosted disabled-buffer reader two");
119 const bool firstStarted = first->
start();
120 const bool secondStarted = second->
start();
121 const bool started = firstStarted && secondStarted;
123 started && waitUntilBlocked(first, firstContext) && waitUntilBlocked(second, secondContext);
124 buffer.disableReads();
125 const bool returned = blocked && waitUntilReturned(firstContext, secondContext);
128 if ((firstStarted || secondStarted) && !returned) {
129 buffer.disableWrites();
136 if (!secondStarted) {
140 return started && blocked && returned && firstJoined && secondJoined &&
141 firstContext.entered == 1 && firstContext.returned == 1 && firstContext.result == 0 &&
142 secondContext.entered == 1 && secondContext.returned == 1 && secondContext.result == 0;
145bool blockedWriterReturnsWhenWritesAreDisabled() {
147 const uint8_t firstValue = 0x51;
148 const bool filled = buffer.write(&firstValue, 1,
false) == 1;
149 BufferWaitContext firstContext(&buffer, BufferOperation::Write, 0x7a);
150 BufferWaitContext secondContext(&buffer, BufferOperation::Write, 0x39);
152 nullptr,
false,
true,
true);
154 &secondContext,
nullptr,
false,
true,
true);
155 first->setName(
"hosted disabled-buffer writer one");
156 second->setName(
"hosted disabled-buffer writer two");
158 const bool firstStarted = filled && first->
start();
159 const bool secondStarted = filled && second->
start();
160 const bool started = firstStarted && secondStarted;
162 started && waitUntilBlocked(first, firstContext) && waitUntilBlocked(second, secondContext);
163 buffer.disableWrites();
164 const bool returned = blocked && waitUntilReturned(firstContext, secondContext);
167 if ((firstStarted || secondStarted) && !returned) {
168 buffer.disableReads();
175 if (!secondStarted) {
179 uint8_t observed = 0;
180 const bool preserved =
181 returned && buffer.read(&observed, 1,
false) == 1 && observed == firstValue;
182 return filled && started && blocked && returned && firstJoined && secondJoined &&
183 firstContext.entered == 1 && firstContext.returned == 1 && firstContext.result == 0 &&
184 secondContext.entered == 1 && secondContext.returned == 1 && secondContext.result == 0 &&
188bool blockedCanReadReturnsWhenWritesAreDisabled() {
190 BufferWaitContext firstContext(&buffer, BufferOperation::CanRead, 0);
191 BufferWaitContext secondContext(&buffer, BufferOperation::CanRead, 0);
193 nullptr,
false,
true,
true);
195 &secondContext,
nullptr,
false,
true,
true);
196 first->setName(
"hosted closed-writer readiness waiter one");
197 second->setName(
"hosted closed-writer readiness waiter two");
199 const bool firstStarted = first->
start();
200 const bool secondStarted = second->
start();
201 const bool started = firstStarted && secondStarted;
203 started && waitUntilBlocked(first, firstContext) && waitUntilBlocked(second, secondContext);
204 buffer.disableWrites();
205 const bool returned = blocked && waitUntilReturned(firstContext, secondContext);
208 if ((firstStarted || secondStarted) && !returned) {
209 buffer.disableReads();
216 if (!secondStarted) {
220 return started && blocked && returned && firstJoined && secondJoined &&
221 firstContext.entered == 1 && firstContext.returned == 1 && firstContext.result == 0 &&
222 secondContext.entered == 1 && secondContext.returned == 1 && secondContext.result == 0;
225bool blockedCanWriteReturnsWhenReadsAreDisabled() {
227 const uint8_t value = 0x24;
228 const bool filled = buffer.write(&value, 1,
false) == 1;
229 BufferWaitContext firstContext(&buffer, BufferOperation::CanWrite, 0);
230 BufferWaitContext secondContext(&buffer, BufferOperation::CanWrite, 0);
232 nullptr,
false,
true,
true);
234 &secondContext,
nullptr,
false,
true,
true);
235 first->setName(
"hosted closed-reader readiness waiter one");
236 second->setName(
"hosted closed-reader readiness waiter two");
238 const bool firstStarted = filled && first->
start();
239 const bool secondStarted = filled && second->
start();
240 const bool started = firstStarted && secondStarted;
242 started && waitUntilBlocked(first, firstContext) && waitUntilBlocked(second, secondContext);
243 buffer.disableReads();
244 const bool returned = blocked && waitUntilReturned(firstContext, secondContext);
247 if ((firstStarted || secondStarted) && !returned) {
248 buffer.disableWrites();
255 if (!secondStarted) {
259 return filled && started && blocked && returned && firstJoined && secondJoined &&
260 firstContext.entered == 1 && firstContext.returned == 1 && firstContext.result == 0 &&
261 secondContext.entered == 1 && secondContext.returned == 1 && secondContext.result == 0 &&
262 buffer.getDataSize() == 1;
266bool runHostedBufferRegressions() {
267 const bool availableWritePassed = availableWriteWaitsForLockOwnership();
268 if (availableWritePassed) {
269 NOTICE(
"HOSTED-WAIT-TEST: PASS buffer-write-available-contention");
271 ERROR(
"HOSTED-WAIT-TEST: FAIL buffer-write-available-contention: contended byte was lost");
274 const bool readerPassed = blockedReaderReturnsWhenReadsAreDisabled();
276 NOTICE(
"HOSTED-WAIT-TEST: PASS buffer-disable-reads-wake");
278 ERROR(
"HOSTED-WAIT-TEST: FAIL buffer-disable-reads-wake: a blocked reader was not released");
281 const bool writerPassed = blockedWriterReturnsWhenWritesAreDisabled();
283 NOTICE(
"HOSTED-WAIT-TEST: PASS buffer-disable-writes-wake");
285 ERROR(
"HOSTED-WAIT-TEST: FAIL buffer-disable-writes-wake: a blocked writer was not released");
288 const bool canReadPassed = blockedCanReadReturnsWhenWritesAreDisabled();
290 NOTICE(
"HOSTED-WAIT-TEST: PASS buffer-can-read-peer-close");
293 "HOSTED-WAIT-TEST: FAIL buffer-can-read-peer-close: empty readiness requeued after "
297 const bool canWritePassed = blockedCanWriteReturnsWhenReadsAreDisabled();
298 if (canWritePassed) {
299 NOTICE(
"HOSTED-WAIT-TEST: PASS buffer-can-write-peer-close");
302 "HOSTED-WAIT-TEST: FAIL buffer-can-write-peer-close: full readiness requeued after reads "
306 return availableWritePassed && readerPassed && writerPassed && canReadPassed && canWritePassed;
static Scheduler & instance()
bool getWaitDebugInfo(WaitDebugInfo &info)
DebugState getDebugState(uintptr_t &address)