Summary
On STC shutdown (after a CGI endpoint had been faulting during the session), the address space terminates with an S33E and an SVC dump. Two distinct problems appear in the log; this issue tracks the shutdown lifecycle one (the repeated MVSMF99E is tracked separately — see Follow-up).
Observed
HTTPD060I SHUTDOWN worker(11CC00) TCB(9BB9D8) ...
MVSMF99E Handler abend S0C4 U0000 for PUT /zosmf/restconsoles/consoles/defcn (x8, same URI)
(~16 s silence)
__CRTGET CRT for TCB(009B29E8) was not found in PPA(000CEFD0) (repeated)
ABEND S33E detected for module 00000000 TCB=009B29E8
PSW:00000000 00000000 KEY(0) MODE(SUP) ILC(2) CC(0)
Traceback interrupted, Forward from PPA 002BEFF0
-> address space terminated, SVC dump taken
Root cause
1. Teardown DETACHes tasks without proving they terminated.
cthread_manager_term → dispatch_thread_term → cthread_worker_shutdown → cthread_detach issues DETACH ...,STAE=YES on the escalation path even when the target is still active / already detached / already reclaimed → S33E. The ATTACH supplies ECB=(task->termecb) (posted 0x40000000 when the subtask actually ends), and the join loops wait on it, but the detach decision itself is not gated on it. @@cmterm.c already documents this failure mode:
force-DETACHes a still-active worker (S33E) and the worker's ESTAE then faults on the torn-down save area (nested S0C4).
The ~16 s of silence is the STIMER WAIT escalation loops (@@cmwshu.c waits 50 x 0.10 s per force-detached worker; @@cmterm.c join loops run tens of seconds) trying to drain workers that won't drain.
2. Recovery ESTAEs do runtime-dependent work at termination.
The worker ESTAE (libc370 @@abrpt.c recovery()) is created ESTAE ...,TERM=YES but never inspects SDWACLUP (clibsdwa.h:379 — "ON INDICATES RECOVERY EXIT ONLY TO CLEANUP AND NOT RETRY"), and it calls the C runtime (wtof, sprintf, and try() → __crtget, estae, GETMAIN) for every register (dump_regs) and every save area (dump_sa). Driven during termination under a TCB whose libc370 CRT is already torn down, each try() → __crtget() fails and logs __CRTGET ... not found in PPA — hence the repeated spam. (The zero PSW/regs is correct output for an RTM/DETACH-detected S33E, which carries no faulting-instruction PSW — not a second bug.) mvsMF's per-request ESTAE failed() in @@try.c has the same defect (ignores SDWACLUP, unconditionally requests retry).
This is the same class as mvslovers/mvsmf#101 (SE37 during fclose in ESTAE recovery): a cleanup path that assumes a healthy runtime and takes a secondary fault.
Ordering note
HTTPD100I CONS(0) STOP before the worker shutdowns is expected and correct — that CONS is the operator console / CIB interface, serviced only by the main thread (httpcons.c), and is the trigger for shutdown, not a resource the workers reference during cleanup. The real race is inside worker teardown (detach vs task termination), not the console.
Proposed fix
- Gate DETACH on proof of death. Treat
task->termecb & 0x40000000 as the single source of truth for "this TCB is gone"; issue DETACH only after it posts. Make cthread_detach idempotent and owner/termination-checked (verify the TCB is still a subtask of the caller and has posted termecb before detaching; skip otherwise).
- Never
STAE=YES-detach a live worker. If a worker won't drain, mark it STUCK, leave its storage, and let address-space termination reclaim it (log the leak) — the same "retain rather than risk use-after-free" tradeoff cthread_manager_term already makes for the dispatch thread.
- Harden the recovery exits. In
@@abrpt.c recovery() and @@try.c failed(): check SDWACLUP first; if set, emit at most one WTO from a pre-formatted buffer and return RC=0 ("continue with termination") — no retry, no dump/register walk, no malloc/try/__crtget. Guard the dump path against a missing CRT.
Notes / next steps
- Confirm the detaching TCB (
009B29E8) by matching against the HTTPD061I STARTING / HTTPD060I SHUTDOWN TCB(...) log lines (dispatch thread vs main task) — this distinguishes double-detach vs cross-owner vs detach-after-reclaim.
- The recovery-exit hardening likely belongs in libc370 (
@@abrpt.c, @@try.c); coordinate with mvsmf#101.
- Follow-up (separate issue): the repeated
MVSMF99E on the same URI is a faulting/wedged endpoint whose retries spread across the whole worker pool — the message data is per-client (httpc->env), not a shared field. It may be compounded by a shared grtapp2 handoff (the GRT is inherited/shared down the task tree — @@crtset.c). Instrument grt/&grtapp2/httpc per CGI invocation to decide: different httpc per worker = mvsMF endpoint bug; same grtapp2/httpc across concurrent workers = shared-slot stomp.
Summary
On STC shutdown (after a CGI endpoint had been faulting during the session), the address space terminates with an
S33Eand an SVC dump. Two distinct problems appear in the log; this issue tracks the shutdown lifecycle one (the repeatedMVSMF99Eis tracked separately — see Follow-up).Observed
Root cause
1. Teardown DETACHes tasks without proving they terminated.
cthread_manager_term→dispatch_thread_term→cthread_worker_shutdown→cthread_detachissuesDETACH ...,STAE=YESon the escalation path even when the target is still active / already detached / already reclaimed → S33E. The ATTACH suppliesECB=(task->termecb)(posted0x40000000when the subtask actually ends), and the join loops wait on it, but the detach decision itself is not gated on it.@@cmterm.calready documents this failure mode:The ~16 s of silence is the
STIMER WAITescalation loops (@@cmwshu.cwaits 50 x 0.10 s per force-detached worker;@@cmterm.cjoin loops run tens of seconds) trying to drain workers that won't drain.2. Recovery ESTAEs do runtime-dependent work at termination.
The worker ESTAE (
libc370 @@abrpt.c recovery()) is createdESTAE ...,TERM=YESbut never inspectsSDWACLUP(clibsdwa.h:379— "ON INDICATES RECOVERY EXIT ONLY TO CLEANUP AND NOT RETRY"), and it calls the C runtime (wtof,sprintf, andtry()→__crtget,estae,GETMAIN) for every register (dump_regs) and every save area (dump_sa). Driven during termination under a TCB whose libc370 CRT is already torn down, eachtry()→__crtget()fails and logs__CRTGET ... not found in PPA— hence the repeated spam. (The zero PSW/regs is correct output for an RTM/DETACH-detectedS33E, which carries no faulting-instruction PSW — not a second bug.) mvsMF's per-request ESTAEfailed()in@@try.chas the same defect (ignoresSDWACLUP, unconditionally requests retry).This is the same class as mvslovers/mvsmf#101 (SE37 during
fclosein ESTAE recovery): a cleanup path that assumes a healthy runtime and takes a secondary fault.Ordering note
HTTPD100I CONS(0) STOPbefore the worker shutdowns is expected and correct — that CONS is the operator console / CIB interface, serviced only by the main thread (httpcons.c), and is the trigger for shutdown, not a resource the workers reference during cleanup. The real race is inside worker teardown (detach vs task termination), not the console.Proposed fix
task->termecb & 0x40000000as the single source of truth for "this TCB is gone"; issueDETACHonly after it posts. Makecthread_detachidempotent and owner/termination-checked (verify the TCB is still a subtask of the caller and has postedtermecbbefore detaching; skip otherwise).STAE=YES-detach a live worker. If a worker won't drain, mark itSTUCK, leave its storage, and let address-space termination reclaim it (log the leak) — the same "retain rather than risk use-after-free" tradeoffcthread_manager_termalready makes for the dispatch thread.@@abrpt.c recovery()and@@try.c failed(): checkSDWACLUPfirst; if set, emit at most one WTO from a pre-formatted buffer and return RC=0 ("continue with termination") — no retry, no dump/register walk, nomalloc/try/__crtget. Guard the dump path against a missing CRT.Notes / next steps
009B29E8) by matching against theHTTPD061I STARTING / HTTPD060I SHUTDOWN TCB(...)log lines (dispatch thread vs main task) — this distinguishes double-detach vs cross-owner vs detach-after-reclaim.@@abrpt.c,@@try.c); coordinate with mvsmf#101.MVSMF99Eon the same URI is a faulting/wedged endpoint whose retries spread across the whole worker pool — the message data is per-client (httpc->env), not a shared field. It may be compounded by a sharedgrtapp2handoff (the GRT is inherited/shared down the task tree —@@crtset.c). Instrumentgrt/&grtapp2/httpcper CGI invocation to decide: differenthttpcper worker = mvsMF endpoint bug; samegrtapp2/httpcacross concurrent workers = shared-slot stomp.