From 7c42af879024eee76e25608dae56f9cea4f02064 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Sun, 6 Sep 2026 19:03:45 +0200 Subject: [PATCH] feat: extend pipe with pipeWrite and pipeHasData --- src/pipe.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/pmm.c | 21 +++++++++ 2 files changed, 151 insertions(+) diff --git a/src/pipe.c b/src/pipe.c index 683ab54..2de2f73 100644 --- a/src/pipe.c +++ b/src/pipe.c @@ -159,3 +159,133 @@ int64 pipeRead(int handle, void* buffer, uint64 length) if (p->writers == 0 && p->everHadWriter) 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; +} diff --git a/src/pmm.c b/src/pmm.c index 0a670b5..4a03bf1 100644 --- a/src/pmm.c +++ b/src/pmm.c @@ -96,9 +96,30 @@ void pmmInit(uint64 multibootInfo, uint64 protectedEnd) 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; if (protectedEnd > bitmapAnchor) bitmapAnchor = protectedEnd; + if (endAddr > bitmapAnchor) + bitmapAnchor = endAddr; bitmap = (uint8*)alignUp(bitmapAnchor, frameSize); uint64 bitmapBytes = (frameCount + 7) / 8; memset(bitmap, 0xFF, bitmapBytes);