feat: add SYS_FUTEX_WAIT and SYS_FUTEX_WAKE syscalls; revamp desktop application categories
This commit is contained in:
1 parent
60ca9c78c7
commit
abfd9de8f6
35 files changed
+545
-70
No files matched your search
@@ -514,6 +514,8 @@ namespace Sched {
|
||||
proc.joinerSlot = -1;
|
||||
proc.exitCode = 0;
|
||||
proc.joinable = false; // main thread is reaped by BSP, not joined
|
||||
proc.futexAddr = 0;
|
||||
proc.futexWoken = false;
|
||||
proc.savedRsp = (uint64_t)sp;
|
||||
proc.stackBase = (uint64_t)kernelStackBase;
|
||||
proc.entryPoint = entry;
|
||||
@@ -824,6 +826,8 @@ namespace Sched {
|
||||
thr.exitCode = 0;
|
||||
thr.joinable = true;
|
||||
thr.threadArg = arg;
|
||||
thr.futexAddr = 0;
|
||||
thr.futexWoken = false;
|
||||
thr.savedRsp = (uint64_t)sp;
|
||||
thr.stackBase = (uint64_t)kernelStackBase;
|
||||
thr.entryPoint = entry;
|
||||
@@ -2116,6 +2120,129 @@ namespace Sched {
|
||||
}
|
||||
}
|
||||
|
||||
// Monotonic enrollment counter for FIFO futex wakeups. Only touched
|
||||
// under schedLock.
|
||||
static uint64_t futexNextSeq = 1;
|
||||
|
||||
int FutexWait(uint64_t uaddr, uint32_t expected, uint64_t timeoutNs) {
|
||||
if ((uaddr & 3) != 0) return InvalidArgument;
|
||||
|
||||
ExitIfKilled();
|
||||
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
int slot = cpu ? cpu->currentSlot : -1;
|
||||
if (slot < 0) return GenericFailure;
|
||||
Process& self = processTable[slot];
|
||||
|
||||
// Round up so a short timeout never turns into "check only".
|
||||
uint64_t timeoutMs = 0;
|
||||
if (timeoutNs != FutexWaitForever) {
|
||||
if (timeoutNs == 0) {
|
||||
timeoutMs = 0;
|
||||
} else {
|
||||
timeoutMs = timeoutNs / 1000000ULL + ((timeoutNs % 1000000ULL) ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
schedLock.Acquire();
|
||||
|
||||
// The word is read here, with schedLock held and interrupts off, so
|
||||
// it must not fault: the caller validated (and demand-committed) the
|
||||
// page, and the page walk below re-confirms it is still mapped. An
|
||||
// aligned 32-bit word never straddles a page.
|
||||
if (!Memory::VMM::Paging::IsUserRangeAccessible(self.pml4Phys, uaddr, 4, false)) {
|
||||
schedLock.Release();
|
||||
return BadPointer;
|
||||
}
|
||||
uint32_t current = __atomic_load_n((volatile uint32_t*)uaddr, __ATOMIC_SEQ_CST);
|
||||
if (current != expected) {
|
||||
schedLock.Release();
|
||||
return WouldBlock;
|
||||
}
|
||||
if (timeoutNs == 0) {
|
||||
schedLock.Release();
|
||||
return TimedOut;
|
||||
}
|
||||
|
||||
self.state = ProcessState::Blocked;
|
||||
self.waitingForPid = -1;
|
||||
self.waitingOnObject = nullptr;
|
||||
self.futexAddr = uaddr;
|
||||
self.futexSeq = futexNextSeq++;
|
||||
self.futexWoken = false;
|
||||
self.sleepUntilTick = (timeoutNs == FutexWaitForever)
|
||||
? 0
|
||||
: Timekeeping::GetTicks() + timeoutMs;
|
||||
self.runningOnCpu = -1;
|
||||
SwitchAwayFromBlockedCurrentLocked();
|
||||
|
||||
// Whatever made this thread runnable (FutexWake, the deadline, or a
|
||||
// kill) it is no longer parked; clear the key before this thread can
|
||||
// block for some other reason and be mistaken for a futex waiter.
|
||||
schedLock.Acquire();
|
||||
bool woken = self.futexWoken;
|
||||
self.futexAddr = 0;
|
||||
self.futexWoken = false;
|
||||
schedLock.Release();
|
||||
|
||||
ExitIfKilled();
|
||||
return woken ? Success : TimedOut;
|
||||
}
|
||||
|
||||
int FutexWake(uint64_t uaddr, uint32_t count) {
|
||||
if ((uaddr & 3) != 0 || uaddr == 0) return InvalidArgument;
|
||||
if (count == 0) return 0;
|
||||
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
int slot = cpu ? cpu->currentSlot : -1;
|
||||
if (slot < 0) return GenericFailure;
|
||||
|
||||
schedLock.Acquire();
|
||||
int primary = processTable[slot].primarySlot;
|
||||
if (primary < 0) primary = slot;
|
||||
|
||||
// Gather this process's waiters on uaddr. At most MaxProcesses.
|
||||
int waiters[MaxProcesses];
|
||||
int n = 0;
|
||||
for (int i = 0; i < MaxProcesses; i++) {
|
||||
Process& p = processTable[i];
|
||||
if (p.state != ProcessState::Blocked) continue;
|
||||
if (p.futexAddr != uaddr) continue;
|
||||
if (p.primarySlot != primary) continue;
|
||||
waiters[n++] = i;
|
||||
}
|
||||
|
||||
// Waking everyone needs no ordering; otherwise pick oldest-first.
|
||||
bool ordered = (uint32_t)n > count;
|
||||
int woken = 0;
|
||||
while (woken < n && (uint32_t)woken < count) {
|
||||
if (ordered) {
|
||||
int best = woken;
|
||||
for (int j = woken + 1; j < n; j++) {
|
||||
if (processTable[waiters[j]].futexSeq < processTable[waiters[best]].futexSeq)
|
||||
best = j;
|
||||
}
|
||||
int tmp = waiters[woken]; waiters[woken] = waiters[best]; waiters[best] = tmp;
|
||||
}
|
||||
|
||||
Process& p = processTable[waiters[woken]];
|
||||
p.futexAddr = 0;
|
||||
p.futexWoken = true;
|
||||
p.sleepUntilTick = 0;
|
||||
p.state = ProcessState::Ready;
|
||||
readyCount++;
|
||||
woken++;
|
||||
}
|
||||
schedLock.Release();
|
||||
|
||||
if (woken > 0) {
|
||||
// Re-read: this thread may have migrated since `cpu` was sampled.
|
||||
auto* here = Smp::GetCurrentCpuData();
|
||||
KickOneIdleCpu(here ? here->cpuIndex : -1);
|
||||
}
|
||||
return woken;
|
||||
}
|
||||
|
||||
bool IsAlive(int pid) {
|
||||
for (int i = 0; i < MaxProcesses; i++) {
|
||||
if (processTable[i].pid == pid) {
|
||||
|
||||
Reference in new issue
Block a user