diff --git a/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.3.tm b/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.4.tm similarity index 99% rename from src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.3.tm rename to src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.4.tm index 4f64e822..d060aced 100644 --- a/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.3.tm +++ b/src/vfs/_vfscommon.vfs/modules/shellfilter-0.2.4.tm @@ -2813,8 +2813,11 @@ namespace eval shellfilter { set syslog [dict get $opts -syslog] dict unset opts -syslog set runtag "shellfilter-run" - set tid [::shellfilter::log::open $runtag [list -syslog 127.0.0.1:514]] - #set tid [::shellfilter::log::open $runtag [list -syslog $syslog]] + #honor the -syslog option (default empty) - the previous hardcoded 127.0.0.1:514 was a + #debug leftover. With no -syslog and no -file the runtag log worker is a noop (no thread + #is created). Syslog remains supported when explicitly configured; see G-036 for the + #Tcl 9 console+udp worker wedge that the always-on syslog worker exposed on exit/quit. + set tid [::shellfilter::log::open $runtag [list -syslog $syslog]] log::info {::shellfilter::log::write $runtag " opts: $opts"} log::info {::shellfilter::log::write $runtag " commandlist:'$commandlist' listlen:$listlen strlen:[string length $commandlist]"} @@ -3827,5 +3830,5 @@ namespace eval shellfilter { package provide shellfilter [namespace eval shellfilter { variable version - set version 0.2.3 + set version 0.2.4 }] diff --git a/src/vfs/_vfscommon.vfs/modules/shellthread-1.6.2.tm b/src/vfs/_vfscommon.vfs/modules/shellthread-1.6.3.tm similarity index 97% rename from src/vfs/_vfscommon.vfs/modules/shellthread-1.6.2.tm rename to src/vfs/_vfscommon.vfs/modules/shellthread-1.6.3.tm index 79d12149..cd0c83c6 100644 --- a/src/vfs/_vfscommon.vfs/modules/shellthread-1.6.2.tm +++ b/src/vfs/_vfscommon.vfs/modules/shellthread-1.6.3.tm @@ -788,8 +788,12 @@ namespace eval shellthread::manager { foreach workertid $subscriberless_workers { if {$workertid ni $shuttingdown_workers} { if {$workertid ni $free_threads && $workertid ne "noop"} { - #JMN - thread::send $workertid {set ::shellthread::worker::settings $::shellthread::worker::settings_defaults} + #Must be async: unsubscribe is on the exit/quit teardown path and a worker whose + #event loop has stopped servicing events (G-036: Tcl 9 console+udp worker wedge) + #would block a synchronous send forever, freezing the shell. Async is safe - + #sends to a thread are processed FIFO, so a later reuse via new_worker sees the + #settings reset applied before anything it sends. + thread::send -async $workertid {set ::shellthread::worker::settings $::shellthread::worker::settings_defaults} #todo - log freeing up of thread lappend free_threads $workertid } @@ -812,7 +816,10 @@ namespace eval shellthread::manager { } proc get_tag_config {tag} { - #review + #review - bare synchronous thread::send: blocks forever if the worker's event loop is + #wedged (G-036 hazard class). Reached from new_worker when a tag already has a live + #worker. Needs a result so can't simply go async - convert to async + vwait-with-timeout + #if this path shows up in a hang. variable workers if {![dict exists $workers $tag]} { error "shellthread::manager::get_tag_config error no existing tag $tag" @@ -858,10 +865,13 @@ namespace eval shellthread::manager { set timedout 1 break } else { - after cancel $timeout_timer + #Do not cancel the timeout timer until all awaited workers have responded - + #cancelling on the first response left later vwait iterations unbounded, so a + #wedged worker (G-036) after a healthy one hung shutdown forever. lappend ended $::shellthread::waitfor } } + after cancel $timeout_timer } set free_threads [list] return [dict create existed $waiting_for ended $ended timedout $timedout allthreads [thread::names]] @@ -963,7 +973,7 @@ namespace eval shellthread::manager { package provide shellthread [namespace eval shellthread { variable version - set version 1.6.2 + set version 1.6.3 }]