Browse Source

G-036 progress: upstream recon, adopt-stdin probe null result, native-stacks next

TEMP_REFERENCE checkouts confirmed current (Tcl core 2026-07-07, tcludp
2026-07-05). core-9-0-branch carries no tclWinConsole.c fix since 9.0.2, so
the wedge - if in the console driver - is likely still present upstream.
Current driver architecture recorded (process-global gConsoleLock, console
handle list shared across all Tcl threads, per-handle SRWLOCKs, reader/writer
threads with CONDITION_VARIABLE wakeups + Tcl_ThreadAlert fan-out) as the
credible wedge surface. Worker-side hypothesis sharpened: std-channel init on
a worker's first channel creation (udp_open / log-file open) attaches it to
the shared console machinery when the process stdin handle is the console -
fits which workers wedge vs survive, though probes have not yet reproduced.
Probe 'adopt' variant added (main thread closes stdin, CONIN$ adopted into
the stdin slot, SetStdHandle, active reader) - clean, ruling out the last
easy stdin-arrangement candidate. Approach reordered: native thread stacks
of a wedged in-context specimen promoted to step 1 (no debugger installed
yet - tool options noted).

Assisted-by: harness=claude; primary-model=claude-fable-5; api-location=anthropic.com
master
Julian Noble 1 week ago
parent
commit
1765004709
  1. 69
      goals/G-036-tcl9-udp-console-worker-wedge.md

69
goals/G-036-tcl9-udp-console-worker-wedge.md

@ -61,6 +61,32 @@ Ingredients ruled out by standalone probes (all clean under a hidden console on
- + 30 datagrams and a later send on the ICMP-flagged socket
- + CONIN$ opened in the main thread with a readable handler armed
- + twapi::SetStdHandle(-10, console) before worker creation
- + main thread closing stdin and ADOPTING the CONIN$ channel into the stdin std-channel
slot (channel name becomes 'stdin'), SetStdHandle, and an active gets-on-readable reader
armed - i.e. the same visible stdin arrangement the restarted repl has ('adopt' probe
variant, 2026-07-08)
Upstream reconnaissance (2026-07-08, TEMP_REFERENCE fossil checkouts: Tcl core trunk-side
branch @ 2026-07-07, tcludp trunk @ 2026-07-05):
- core-9-0-branch has had NO tclWinConsole.c change since 9.0.2 beyond an include-hygiene
commit (2025-10-24) - the wedge, if in the console driver, is likely still present in
9.0.x maintenance. Trunk has console-channel activity by apnadkarni (2025-12-29
metakey-notification change + revert; branch apn-winconsole-noreadahead) worth reading.
- Current tclWinConsole.c architecture confirms a credible wedge surface: process-global
gConsoleLock, a console-handle list SHARED ACROSS ALL Tcl THREADS, per-handle SRWLOCKs,
reader/writer threads coordinated via CONDITION_VARIABLEs (consoleThreadCV /
interpThreadCV "for awakening interp thread(s)"), and Tcl_ThreadAlert fan-out to
interested channel-owning threads. A missed CV wakeup leaves an interp thread blocked
inside a console-driver call - matching the observed "blocked not spinning, timers and
thread::sends unserviced".
- Sharpened worker-side ingredient hypothesis: a fresh thread's std channels initialize on
its FIRST channel creation (udp_open, or a -file log open) - with the process stdin
handle pointing at the console (post SetStdHandle), that first channel creation attaches
the worker to the shared console machinery. Fits the data exactly: run-2 workers that
never create any channel (-file {} -syslog {}) survive; channel-creating ones wedge;
run-1 workers (stdin handle = dead pipe at creation) are immune. NOT yet reproduced by
probes, so at least one more live-shell ingredient is still missing (candidates below).
Untested ingredients (next candidates):
@ -79,12 +105,20 @@ per-handle reader/writer threads + gConsoleLock) or tcludp's Windows event sourc
## Approach
1. Complete the minimal repro by adding the untested ingredients one at a time to the standalone
probe (console read pumping and console output channels first - they are what the live shell
has and every clean probe lacked).
2. If no single addition wedges, capture native thread stacks of a wedged in-context specimen
(spawn via the hidden-console repro below; it idles indefinitely) and work backwards from the
blocked wait.
1. Capture native thread stacks of a wedged in-context specimen and work backwards from the
blocked wait - promoted to first step after seven single-ingredient probe variants all came
up clean (ingredient bisection is guessing; the stack names the exact wait). Spawn a specimen
via the hidden-console repro below (post-fix, punkshout/punksherr still wedge in run 2 - their
syslog comes from the logging config, and they idle indefinitely as specimens). Needs a native
tool: none installed as of 2026-07-08 (no procdump/cdb/windbg) - e.g.
`winget install Microsoft.WinDbg` or Sysinternals procdump (user's call), or
`rundll32 C:\Windows\System32\comsvcs.dll, MiniDump <pid> <dumpfile> full` writes a dump
without extra tools but analysis still needs a debugger. Read stacks against
tclWinConsole.c's CV waits and tclWinNotify.c.
2. Continue minimal-repro construction guided by what the stacks reveal (remaining untried
live-shell ingredients: codethread presence, fifo2 tee stack with thread::transfer'd halves,
punk::console raw/line + VT SetConsoleMode churn and -inputmode queries, console output
channels, multi-thread channel-creation concurrency).
3. Write up the mechanism here; reduce the repro accordingly.
4. Re-verify against a Tcl 9 built from current core sources before any upstream report (the
kit's 9.0.2 may carry an already-fixed bug). Building Tcl 9 from source is G-005 territory;
@ -174,8 +208,8 @@ Write-Output "batch done"
### Standalone probe - udpthread_test.tcl (all variants clean so far)
Run: `punk902z src script udpthread_test.tcl [conin] [setstd] [twapi]` - directly (consoleless)
or via hidden console (`Start-Process cmd.exe -WindowStyle Hidden -ArgumentList '/c','...exe src
Run: `punk902z src script udpthread_test.tcl [adopt|conin [setstd]] [twapi]` - directly
(consoleless) or via hidden console. (`Start-Process cmd.exe -WindowStyle Hidden -ArgumentList '/c','...exe src
script ...tcl conin setstd > out 2>&1'`). Diag appends to `%TEMP%\udpthread_diag.txt`.
```tcl
@ -193,6 +227,25 @@ proc d {msg} {
}
d "=== run start (tcl [info patchlevel]) argv:'$::argv' ==="
# mimic punkshell's actual restart: close stdin so the CONIN$ channel is ADOPTED into the
# stdin std-channel slot, then read it like the repl reader does
if {"adopt" in $::argv} {
if {[catch {
close stdin
set ::conin [open {CONIN$} r]
package require twapi
set h [twapi::get_tcl_channel_handle $::conin in]
twapi::SetStdHandle -10 $h
chan configure $::conin -blocking 0
chan event $::conin readable [list apply {{c} {
if {[gets $c line] < 0 && [eof $c]} {chan event $c readable {}}
}} $::conin]
d "stdin closed, CONIN\$ adopted as '$::conin' (stdin slot), SetStdHandle done, reader armed"
} err]} {
d "adopt setup FAILED: $err"
}
}
# mimic the restarted repl: main thread opens CONIN$ and arms a readable handler
if {"conin" in $::argv} {
if {[catch {

Loading…
Cancel
Save