feat: implement new IPC layer
This commit is contained in:
+107
-29
@@ -1,37 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Ipc/Ipc.hpp>
|
||||
|
||||
namespace Montauk {
|
||||
// Find the process that owns the I/O ring buffers for a redirected process.
|
||||
// If proc owns buffers itself (spawned via spawn_redir), returns proc.
|
||||
// If proc inherited redirection (spawned via spawn from a redirected parent),
|
||||
// follows parentPid to find the buffer owner.
|
||||
|
||||
static Sched::Process* GetRedirTarget(Sched::Process* proc) {
|
||||
if (!proc || !proc->redirected) return nullptr;
|
||||
if (proc->outBuf) return proc; // owns buffers
|
||||
return Sched::GetProcessByPid(proc->parentPid);
|
||||
}
|
||||
|
||||
// SPSC ring buffer helpers. On x86 TSO, atomic-width aligned
|
||||
// loads/stores are naturally atomic. The compiler barrier ensures
|
||||
// the data write is visible before the head update.
|
||||
static void RingWrite(uint8_t* buf, volatile uint32_t& head, uint32_t /*tail*/, uint32_t size, uint8_t byte) {
|
||||
uint32_t h = head;
|
||||
buf[h] = byte;
|
||||
asm volatile("" ::: "memory"); // compiler barrier
|
||||
head = (h + 1) % size;
|
||||
}
|
||||
|
||||
static int RingRead(uint8_t* buf, volatile uint32_t& head, volatile uint32_t& tail, uint32_t size, uint8_t* out, int maxLen) {
|
||||
int count = 0;
|
||||
uint32_t t = tail;
|
||||
uint32_t h = head;
|
||||
while (t != h && count < maxLen) {
|
||||
out[count++] = buf[t];
|
||||
t = (t + 1) % size;
|
||||
if (proc->ioOutHandle >= 0 || proc->ioInHandle >= 0 || proc->ioKeyHandle >= 0) {
|
||||
return proc;
|
||||
}
|
||||
asm volatile("" ::: "memory"); // compiler barrier
|
||||
tail = t;
|
||||
return count;
|
||||
return (proc->parentPid >= 0) ? Sched::GetProcessByPid(proc->parentPid) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ResolveProcessHandle(Sched::Process* proc, int handle, Ipc::HandleType expectedType,
|
||||
Ipc::Object*& outObject, uint32_t* outRights = nullptr) {
|
||||
if (proc == nullptr || handle < 0) return false;
|
||||
|
||||
int slot = Ipc::SlotForPid(proc->pid);
|
||||
if (slot < 0) return false;
|
||||
|
||||
Ipc::HandleType type = Ipc::HandleType::None;
|
||||
Ipc::Object* object = nullptr;
|
||||
uint32_t rights = 0;
|
||||
if (!Ipc::SnapshotHandleForSlot(slot, handle, type, object, rights)) return false;
|
||||
if (type != expectedType || object == nullptr) return false;
|
||||
|
||||
outObject = object;
|
||||
if (outRights != nullptr) *outRights = rights;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Ipc::Stream* GetRedirOutStream(Sched::Process* proc) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioOutHandle, Ipc::HandleType::Stream, object)) return nullptr;
|
||||
return (Ipc::Stream*)object;
|
||||
}
|
||||
|
||||
static Ipc::Stream* GetRedirInStream(Sched::Process* proc) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioInHandle, Ipc::HandleType::Stream, object)) return nullptr;
|
||||
return (Ipc::Stream*)object;
|
||||
}
|
||||
|
||||
static Ipc::Mailbox* GetRedirKeyMailbox(Sched::Process* proc) {
|
||||
proc = GetRedirTarget(proc);
|
||||
if (proc == nullptr) return nullptr;
|
||||
|
||||
Ipc::Object* object = nullptr;
|
||||
if (!ResolveProcessHandle(proc, proc->ioKeyHandle, Ipc::HandleType::Mailbox, object)) return nullptr;
|
||||
return (Ipc::Mailbox*)object;
|
||||
}
|
||||
|
||||
static int DuplicateHandleBetweenSlots(int srcSlot, int handle, int dstSlot) {
|
||||
if (srcSlot < 0 || dstSlot < 0 || handle < 0) return -1;
|
||||
|
||||
Ipc::HandleType type = Ipc::HandleType::None;
|
||||
Ipc::Object* object = nullptr;
|
||||
uint32_t rights = 0;
|
||||
if (!Ipc::SnapshotHandleForSlot(srcSlot, handle, type, object, rights)) return -1;
|
||||
if ((rights & Ipc::RightDup) == 0) return -1;
|
||||
return Ipc::InstallHandleForSlot(dstSlot, object, type, rights);
|
||||
}
|
||||
|
||||
static bool ConfigureRedirWaitsetForSlot(int slot, Sched::Process* proc) {
|
||||
if (slot < 0 || proc == nullptr) return false;
|
||||
|
||||
int waitset = Ipc::CreateWaitsetHandleForSlot(slot);
|
||||
if (waitset < 0) return false;
|
||||
|
||||
if (proc->ioInHandle >= 0) {
|
||||
if (Ipc::WaitsetAddHandleForSlot(slot, waitset, proc->ioInHandle,
|
||||
Ipc::SignalReadable | Ipc::SignalPeerClosed) < 0) {
|
||||
Ipc::CloseHandleForSlot(slot, waitset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc->ioKeyHandle >= 0) {
|
||||
if (Ipc::WaitsetAddHandleForSlot(slot, waitset, proc->ioKeyHandle,
|
||||
Ipc::SignalReadable | Ipc::SignalPeerClosed) < 0) {
|
||||
Ipc::CloseHandleForSlot(slot, waitset);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
proc->ioWaitsetHandle = waitset;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int WriteAllToStream(Ipc::Stream* stream, const uint8_t* data, int len) {
|
||||
if (stream == nullptr || data == nullptr || len <= 0) return -1;
|
||||
|
||||
int total = 0;
|
||||
while (total < len) {
|
||||
int written = Ipc::StreamWrite(stream, data + total, len - total, false);
|
||||
if (written < 0) {
|
||||
return (total > 0) ? total : -1;
|
||||
}
|
||||
if (written == 0) {
|
||||
Sched::BlockOnObject(stream, 0);
|
||||
continue;
|
||||
}
|
||||
total += written;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user