diff options
| author | user <user@clank> | 2026-09-22 17:12:32 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-22 17:12:32 +0200 |
| commit | bb55479e693a04df0da8320adfa10a6b8188b8da (patch) | |
| tree | 9b3736cff55112982d1722bc4c1a9630785aeda9 /src/syscall.asm | |
| parent | fs: bounds-check direct blocks - a file over 2 KiB ate the next inode (diff) | |
| download | gbos-bb55479e693a04df0da8320adfa10a6b8188b8da.tar.gz gbos-bb55479e693a04df0da8320adfa10a6b8188b8da.tar.xz gbos-bb55479e693a04df0da8320adfa10a6b8188b8da.zip | |
kernel: per-process sleep - two sleepers used to deadlock each other
sys_sleep kept its entire state in three globals (wSleepTarget, wSleepAcc,
wSleepLastDiv) and yielded inside its own loop. With two processes sleeping
at once, each zeroed the other's accumulator on entry, so neither ever
reached its target: both slept ~forever.
Nothing hit it while only one program at a time paced itself. `httpd &`
(polling accept) plus a foreground `ping` is enough: ping wedged with its
echo reply already sitting in the socket buffer, unread.
Now the deadline lives in registers - i.e. on the caller's own stack across
SchedYield, which makes it per-process by construction - and it counts the
64 Hz tick (wTicks, via TimerISR) instead of accumulating DIV deltas.
ticks16 re-reads the counter until the high byte is stable, so a carry
between the two byte reads can't report a 256-unit jump.
Diffstat (limited to '')
| -rw-r--r-- | src/syscall.asm | 79 |
1 files changed, 46 insertions, 33 deletions
diff --git a/src/syscall.asm b/src/syscall.asm index cab3e79..e0961bc 100644 --- a/src/syscall.asm +++ b/src/syscall.asm @@ -278,45 +278,56 @@ ENDR ret ; ----------------------------------------------------------------------------- -; sys_sleep(B = 1/64-second units) - cooperative delay driven by the DIV timer. -; DIV free-runs at 16384 Hz - 32768 Hz in double-speed mode, where 256 ticks -; = 1/128 s - so the accumulated high byte is halved before comparing against -; the target. We accumulate DIV deltas across SchedYields (other procs run) -; until enough 1/64-second units have elapsed. +; ticks16 -> HL = low 16 bits of the 64 Hz tick counter, read consistently +; (TimerISR can carry between the two byte reads, which would otherwise read +; $01FF out of a counter going $00FF -> $0100). +; ----------------------------------------------------------------------------- +ticks16: + ld a, [wTicks+1] + ld h, a + ld a, [wTicks] + ld l, a + ld a, [wTicks+1] + cp h + jr nz, ticks16 + ret + +; ----------------------------------------------------------------------------- +; sys_sleep(B = 1/64-second units) - cooperative delay against the system tick. +; +; Every piece of state lives in registers, i.e. on the CALLER'S stack across +; SchedYield, which makes it per-process. The old version accumulated DIV +; deltas into three globals (wSleepTarget/wSleepAcc/wSleepLastDiv) and yielded +; mid-loop: two processes sleeping at the same time each zeroed the other's +; accumulator on entry, so neither ever reached its target and both slept +; forever. That needs only a background daemon plus any foreground tool that +; paces itself - `httpd &` then `ping`. ; ----------------------------------------------------------------------------- sys_sleep: ld a, b or a ret z ; sleep 0 = no-op - ld [wSleepTarget], a - xor a - ld [wSleepAcc], a - ld [wSleepAcc+1], a - ld a, [rDIV] - ld [wSleepLastDiv], a + ld c, a ; C = units requested + call ticks16 + ld d, h + ld e, l ; DE = tick at entry .loop - call SchedYield - ld a, [rDIV] - ld c, a ; current DIV - ld a, [wSleepLastDiv] - ld b, a ; previous DIV - ld a, c - ld [wSleepLastDiv], a - sub b ; A = DIV delta (mod 256) - ld e, a - ld a, [wSleepAcc] ; acc += delta - add e - ld [wSleepAcc], a - ld a, [wSleepAcc+1] - adc 0 - ld [wSleepAcc+1], a ; acc high byte = elapsed 1/128-second units - srl a ; /2: double-speed DIV -> 1/64-second units + push bc + push de + call SchedYield ; other processes run here + pop de + pop bc + call ticks16 ; HL = tick now + ld a, l + sub e ld b, a - ld a, [wSleepTarget] - cp b - jr z, .done ; elapsed == target - jr c, .done ; elapsed > target - jr .loop + ld a, h + sbc d ; BA... = now - start (mod 65536) + or a + jr nz, .done ; elapsed >= 256 units: long past any target + ld a, b + cp c + jr c, .loop ; elapsed < requested .done ret @@ -378,8 +389,9 @@ sys_exit:: ld h, a ld a, [hl] ld [wExitParentPid], a - ; hand any children to init + ; hand any children to init; free any sockets we still hold call ReparentToInit + call SockReleaseOwner ; wake our parent if it's blocked in wait() ld a, [wExitParentPid] call FindPcbByPid ; DE=&parent PCB, CF if gone @@ -532,6 +544,7 @@ sys_kill:: ld a, b ld [wExitMyPid], a call ReparentToInit + call SockReleaseOwner ; the victim cannot close() them itself ; wake victim's parent if it is blocked in wait() ld a, [wKillParent] call FindPcbByPid |
