Skip to main content

Module thread_shell_state

Module thread_shell_state 

Source
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'             <- zsh

The 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:

  • ThreadMutex for the stack-shaped state (scriptname, scriptfilename, funcstack, zsh_eval_context). It mirrors Mutex’s lock() / clear_poison() so the ~50 existing FUNCSTACK.lock() call sites read exactly as they did before.
  • LocalLevelCell for locallevel, which cannot simply become per-thread — see its own docs.

Structs§

LocalLevelCell
locallevel storage: shared for parameter scoping, per-thread for diagnostics.
ThreadMutex
A Mutex whose 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.