The lock-free syscall class (docs/kernel-locking.md step 9): read, write,
readv, writev, pread64, pwrite64, lseek and rt_sigprocmask, decided by
number (syscalls::free_class). The page makes the whole call from its
Worker, blocking included, with the same code the kernel runs (FdIo,
sigprocmask). A call that waits:
- parks in the thread's own run state;
- makes its first scheduler turn itself (pick_mine): it finishes the call,
or registers the slot on the stream's queue.
The idle "why" is per thread and kernel-free (wait_why, HAVE_NET,
FileHandle::is_global_socket). waitq::enter and publish_timer read the
process's signal state, not the kernel. Only the process's end stays the
kernel's (exit_here).
Also deletes 8 sites that read the pid or the ABI (my_pid, compat_abi).
Sites: 183 -> 175.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c
Each thread's pc, wait, parked call and what that call still owes live
behind an Arc<Mutex<Run>> of their own, which the thread's Worker holds
(MY_RUN). The kernel's per-call leftovers (restore_sigmask, write_blocked,
the accept/recvmsg/transfer retries) are taken out as the call parks, so an
idle turn that finds its wait over finishes the call itself: finish_parked,
the answer into its context, SIGPIPE, the sigsuspend mask and delivery
through sigs::finish. A call the kernel used to redo after its wait is made
again from its instruction instead (in place for a wasm32 program or a
provider). A safepoint's handler frame resumes without schedule. The locked
pick stays for a blocking call's first turn (under its own take), the
compositor pump and the deadlock watch; exit stays the kernel's.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c
Conflicts: proc.rs keeps both the process wait queue and the /proc info;
run.sh's PAGE_PROGRAMS has the signal programs and clock-monotonic;
runtime.js passes the kernel with wl-memory. EpollSet::with_watches (a
fork's copy) is now built on new(): its own queue, each watch chained
again as a waiter collects it.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c
Signals is the process's ProcSig (dispositions behind an RwLock, the
shared pending word, the interval timers with an atomic earliest
deadline) and the running thread's ThreadSig (pending and mask as atomic
words). Switching threads swaps an Arc; save_thread/load_thread are gone.
Each page Worker holds its own thread's ThreadSig (sigs.rs). A safepoint
asks whether a signal would do something, collects the inbox, fires due
timers and builds the handler frame with no kernel lock and no hints:
SIGNAL_SEEN, LOCAL_SIGNALS, TIMERS_ARMED and the 64th-safepoint sweep are
deleted, with the locked check and the locked delivery. The handler's
start reaches schedule through HANDLER_AT. waitq's idle turn asks the
signal state instead of the hints.
- The proc table's signal sequence moves after the inbox bit is set
(it moved before, which the sweep hid).
- A delivery claims its signal with one fetch_and before building the
frame, and collecting raises each signal once with its sender: two
raises let a lock-free deliverer take a process-directed signal twice.
- thread-signals' page failure was the test's 100 ms sleep (a page starts
a thread's Worker later); it hands off under the mutex now.
close-while-reading waits for its readers. New signal-storm: targeted
pthread_kill to spinning threads, a shared signal taken exactly once,
masks respected. All three in PAGE_PROGRAMS.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c
The user, 2026-10-12: the plan is removal, not fast paths. §5 now says so,
with the progress metric (lock-taking call sites in shims/browser/src, 232
on master f3689d12, and the per-syscall census), what has landed (steps 0
to 5 and 6a), and the remaining steps ordered by the measured wait of the
diag/klock-6e census: clocks, then waits on Linux's wait-queue model (a
slot per thread registered on every watched object, epoll notified by its
objects, no global WAKE re-check), then the fd table, futexes, per-thread
signal state, the memory map, signals and the thread table, lifecycle, and
native. Each says which structures become concurrent and how.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_ea9f71488b6e448da93d6c2dabf4879c