Expand description
thread_shell_state submodule — per-thread storage for the state C
keeps per thread of execution (scriptname, funcstack,
zsh_eval_context, the diagnostic half of locallevel).
Per-thread storage for the shell state C keeps per THREAD OF EXECUTION.
zshrs-original infrastructure — no Src/*.c counterpart. It is the
generalisation of crate::errflag_cell, and it exists for the same
reason: zshrs replaced a fork(2) with a thread.
C zsh runs on one thread. scriptname (Src/init.c, the diagnostic
prefix zwarning reads at Src/utils.c:147), funcstack
(Src/exec.c:340, behind $funcstack / $functrace /
$funcfiletrace / $funcsourcetrace), zsh_eval_context
(zsh.export:355, behind $ZSH_EVAL_CONTEXT) and locallevel
(Src/params.c:54) are all plain globals that describe WHERE THE ONE
THREAD CURRENTLY IS. Background work in C is a forked child, so the
child’s pushes onto those globals are invisible to the parent.
zshrs runs some of that work on the worker pool (crate::worker)
instead — async_precmd (crate::async_precmd) hands hook FUNCTIONS to
a worker, and compsys::in_editor drives the completer tree on a
dedicated thread. A hook function moves every one of those globals while
it runs, and the shell thread reads the moved value:
async_precmd_functions=(spin_hook) # spin_hook is running on a worker
% done # typed at the prompt
spin_hook:4: parse error near `done' <- zshrs, before this module
zsh: parse error near `done' <- zshThe user is pointed at a function that has nothing to do with what they
typed, which is worse than no information at all. quiesce
(crate::async_precmd::quiesce) closes the window where the shell
thread EXECUTES shell code during a batch, but the line above is
diagnosed by the LEXER, which runs before the quiesce() in
init::loop_ — and quiesce is a serialisation, not a fix for the
sharing.
Restoring the C model is the fix: the shell thread keeps the process-wide value, every other thread gets its own. A thread that runs shell code off the shell thread IS a forked child in the C design, so where it is stays with it.
Two shapes live here:
ThreadMutexfor the stack-shaped state (scriptname,scriptfilename,funcstack,zsh_eval_context). It mirrorsMutex’slock()/clear_poison()so the ~50 existingFUNCSTACK.lock()call sites read exactly as they did before.LocalLevelCellforlocallevel, which cannot simply become per-thread — see its own docs.
Structs§
- Local
Level Cell locallevelstorage: shared for parameter scoping, per-thread for diagnostics.- Thread
Mutex - A
Mutexwhose contents are the shell thread’s on the shell thread and this thread’s own everywhere else.
Functions§
- is_
shell_ thread - True on the shell thread, false on every worker.