feat: extend pipe with pipeWrite and pipeHasData

This commit is contained in:
dualfroz committed 2026-09-06 19:03:45 +02:00
1 parent 43e3013260
commit 7c42af8790
2 files changed
+151

No files matched your search

+130
View File
@@ -159,3 +159,133 @@ int64 pipeRead(int handle, void* buffer, uint64 length)
if (p->writers == 0 && p->everHadWriter) if (p->writers == 0 && p->everHadWriter)
return 0; return 0;
return -1;
}
int64 pipeWrite(int handle, const void* buffer, uint64 length)
{
Pipe* p = pipeAt(handle);
if (!p)
return -1;
/* Symmetric with pipeRead's EOF check above: only a reader that WAS
attached and left counts as a broken pipe. */
if (p->readers == 0 && p->everHadReader)
return -1;
if (length == 0)
return 0;
if (ringIsFull(&p->ring))
return -1;
usize put = ringWrite(&p->ring, (const uint8*)buffer, (usize)length);
/* Writing adds ring data: any reader blocked on "buffer empty" may now
be able to proceed, so give it a chance to recheck. */
schedWakeChannel((const void*)p);
schedWakeChannel(schedPollChannel());
return (int64)put;
}
bool pipeHasData(int handle)
{
Pipe* p = pipeAt(handle);
if (!p)
return false;
return !ringIsEmpty(&p->ring) || (p->writers == 0 && p->everHadWriter);
}
bool pipeHasSpace(int handle)
{
Pipe* p = pipeAt(handle);
if (!p)
return false;
return !ringIsFull(&p->ring);
}
int64 pipeReadBlocking(int handle, void* buffer, uint64 length)
{
Pipe* p = pipeAt(handle);
if (!p)
return -1;
for (;;) {
int64 result = pipeRead(handle, buffer, length);
if (result != -1)
return result;
schedBlockCurrent((const void*)p);
halt();
}
}
int64 pipeWriteBlocking(int handle, const void* buffer, uint64 length)
{
Pipe* p = pipeAt(handle);
if (!p)
return -1;
for (;;) {
int64 result = pipeWrite(handle, buffer, length);
if (result != -1)
return result;
/* A reader that attached and then left is a broken pipe: a terminal
condition no amount of waiting will ever resolve, unlike a merely
full buffer (or a pipeCreateDetached handle whose reader simply
has not attached yet). */
if (pipeWriteBroken(handle))
return -1;
schedBlockCurrent((const void*)p);
halt();
}
}
static void pipeReleaseIfUnused(int handle)
{
Pipe* p = &pipes[handle];
if (p->readers == 0 && p->writers == 0)
p->used = false;
}
void pipeCloseRead(int handle)
{
Pipe* p = pipeAt(handle);
if (!p)
return;
if (p->readers > 0)
p->readers--;
/* A writer parked on "buffer full" must be woken to discover the pipe is
now broken instead of waiting forever for space that will never free
up on its own. */
if (p->readers == 0) {
schedWakeChannel((const void*)p);
schedWakeChannel(schedPollChannel());
}
pipeReleaseIfUnused(handle);
}
void pipeCloseWrite(int handle)
{
Pipe* p = pipeAt(handle);
if (!p)
return;
if (p->writers > 0)
p->writers--;
/* A reader parked on "buffer empty" must be woken to discover true
end-of-file instead of waiting forever for data that will never
arrive. */
if (p->writers == 0) {
schedWakeChannel((const void*)p);
schedWakeChannel(schedPollChannel());
}
pipeReleaseIfUnused(handle);
}
bool pipeDestroyIfUnattached(int handle)
{
Pipe* p = pipeAt(handle);
if (!p || p->readers != 0 || p->writers != 0)
return false;
p->used = false;
return true;
}
+21
View File
@@ -96,9 +96,30 @@ void pmmInit(uint64 multibootInfo, uint64 protectedEnd)
frameCount = highestAddr / frameSize; frameCount = highestAddr / frameSize;
/* The bitmap itself must never overlap live data this function still
needs to read (or that anything else placed physically-here still
needs): the loaded kernel image, the multiboot module the caller has
already claimed via protectedEnd (typically the initrd, see
kmain.c), AND the multiboot info structure this very function is
reading `mmap` out of via endAddr, computed above. That last one is
not academic -- GRUB places the multiboot2 info block immediately
after the last module it loads, so protectedEnd (the module's own
end) and multibootInfo's start can legitimately land on the exact
same address. Anchoring on protectedEnd alone would then place the
bitmap ON TOP of the live mmap tag, and the memset just below would
zero out every entry's baseAddr/length/type with 0xFF *before* the
markFree pass a few lines down ever reads them -- silently losing
the entire usable-memory picture and leaving every frame "used"
forever. Growing the initrd (adding another user program, say) is
exactly what nudges protectedEnd into colliding with endAddr, so
this is not a hypothetical: it reproduced on the very next boot after
tools/mktar's ramdisk grew past whatever margin happened to exist
before. */
uint64 bitmapAnchor = (uint64)kernelPhysEnd; uint64 bitmapAnchor = (uint64)kernelPhysEnd;
if (protectedEnd > bitmapAnchor) if (protectedEnd > bitmapAnchor)
bitmapAnchor = protectedEnd; bitmapAnchor = protectedEnd;
if (endAddr > bitmapAnchor)
bitmapAnchor = endAddr;
bitmap = (uint8*)alignUp(bitmapAnchor, frameSize); bitmap = (uint8*)alignUp(bitmapAnchor, frameSize);
uint64 bitmapBytes = (frameCount + 7) / 8; uint64 bitmapBytes = (frameCount + 7) / 8;
memset(bitmap, 0xFF, bitmapBytes); memset(bitmap, 0xFF, bitmapBytes);