Skip to content

Cancelling HTTPD floods the console with libc370 cleanup-only recovery WTOs — decide how to handle them #258

Description

@mgrossmann

Cancelling the server buries the console under libc370 recovery WTOs. Observed
2026-08-25, a C HTTPD against a running server:

IEE301I HTTPD    CANCEL COMMAND ACCEPTED
libc370 @@@try.c failed(): recovery cleanup-only, retry suppressed
libc370 recovery: abend during task termination, cleanup only
libc370 @@@try.c failed(): recovery cleanup-only, retry suppressed
libc370 recovery: abend during task termination, cleanup only
libc370 @@@try.c failed(): recovery cleanup-only, retry suppressed
libc370 recovery: abend during task termination, cleanup only
libc370 @@@try.c failed(): recovery cleanup-only, retry suppressed
libc370 recovery: abend during task termination, cleanup only
libc370 @@@try.c failed(): recovery cleanup-only, retry suppressed
libc370 recovery: abend during task termination, cleanup only
libc370 @@@try.c failed(): recovery cleanup-only, retry suppressed
libc370 recovery: abend during task termination, cleanup only
libc370 recovery: abend during task termination, cleanup only
IEF450I HTTPD HTTPD - ABEND S222 U0000 - TIME=10.22.49
$HASP395 HTTPD    ENDED

Six @@@try.c failed() lines, seven recovery: lines, thirteen in total, and
none of them is something an operator can act on. IEE301I and
IEF450I … ABEND S222 already say what happened and why. That is exactly the
test CLAUDE.md sets for a WTO, and these lines fail it.

Where they come from

Two sites, both in libc370, both deliberate:

  • src/clib/@@abrpt.c recovery() — the abendrpt(ESTAE_CREATE, …) exit.
    Guarded on SDWAERRD & SDWACLUP: RTM entered only to clean up a terminating
    task, so the register/save-area walk is skipped (it would fault or spew "CRT
    not found" under a torn-down CRT), one CRT-free WTO is written, SETRP(…,0,0,0).
  • src/clib/@@@try.c failed() — the try() / ___try exit. Same guard,
    added in libc370 feat: migrate build system to mbt #16 (v1.0.1) after v1.0.0's [HTTPD] HTTP Request Parsing härten #10 had guarded only the
    unreachable @@try.c copy. Requesting a retry under a terminating TCB is
    invalid, so it suppresses the retry and writes its line.

The guards are correct and must stay. What is in question is only the WTO each
one emits.

httpd registers abendrpt(ESTAE_CREATE, …) in main(), socket_thread() and
worker_thread() (src/httpd.c:248, :781, :1015), and calls try() in a handful
of places — the one that matters for a worker is try(serve_client, httpc, work)
at src/httpd.c:1037.

Open question 1: what produces exactly this count?

This is not yet explained, and the issue should not pretend otherwise. The
tempting reading — one recovery: line per task, plus one @@@try.c line per
task that happened to be inside a try() — does not survive a look at the code:

  • try() creates its ESTAE, calls, and deletes it again before returning
    (call() in @@@try.c, cleanup:), so the exit only exists for the duration
    of the call.
  • A worker between requests waits in cthread_worker_wait() (src/httpd.c:1023),
    which is outside the try() at :1037. libc370's src/thdmgr/ does not use
    try() at all. So an idle pool should contribute no @@@try.c lines, and six
    of them want accounting for.

So either the server was busier than it looked, or something else holds a try()
ESTAE across the wait, or the exits are driven more than once per task during
termination. Settle this before choosing a fix — the answer decides whether
the burst is bounded by the task count or by something that can be much larger.

What is safe to say without it: the burst grows with the worker pool (MAXTASK
up to 9, and the pool never shrinks back — it is a high-water mark) and with
try() nesting depth, since mvsMF's router.c adds its own try()/tryrc()
layer per request.

And this is not a C HTTPD problem. SDWACLUP is set for any termination-time
entry into the exit, so the same burst lands on an S878 or S80A memterm — that
is, precisely when the console is wanted for real diagnosis and not for thirteen
lines of bookkeeping.

The constraint that kills the obvious fix

"Only emit it once per termination burst" is the first idea anyone has, and it
cannot be built where the message is written:

Any suppression state therefore has to be supplied by the caller, which makes
this an API question, not a one-line edit.

Options to weigh

  1. Delete both WTOs. Cheapest, and the fallback if nothing better survives
    scrutiny. Cost: the only evidence that the cleanup-only path was taken at all
    disappears — and that evidence is why [HTTPD] HTTP Request Parsing härten #10/feat: migrate build system to mbt #16 exist.
  2. Make it a caller option. @@abrpt.c already has a channel:
    recovery() reads param->u[1] for the dump option, so a quiet-on-cleanup
    flag alongside DUMP_DEFAULT is cheap and per-registration — httpd's three
    sites opt in, every other consumer is untouched. try() has no options slot
    at all
    , and its PARAM u[1] is already the register block. So the option is
    easy for one of the two messages and not for the other, and what happens to
    the @@@try.c line
    is the second open question of this issue.
  3. Hardcopy-only WTO. Keep the breadcrumb, keep it off the operator console —
    MCSFLAG=HRDCPY. It matches CLAUDE.md's own division of labour: the console
    is for what an operator can act on, the log is the server's diagnostic
    channel. Two things to settle before this can be recommended:
    • Does a hardcopy-only WTO still reach the Master Trace Table? /.dmtt
      and cmtt_get_array() are where these lines would have to remain readable.
      If it does not, this option collapses into option 1 with extra API surface.
      This is the one measurement this issue needs.
    • wto() hardcodes mcsflags = 0 (src/clib/wto.c) and takes no flags
      argument, so this means a new entry point in libc370, not an edit.

Not an option: removing the try() wrapper around serve_client. src/httpd.c:1032
records why it is there — it would trade worker recovery for a quieter console.

Scope

The fix is not in httpd. Both sites are libc370, and every consumer shares
them — ftpd, mvsmf, ufsd, rexx370, httplua, httprexx. This issue is the decision;
the deliverable is the chosen shape plus an implementing issue filed in libc370,
referencing libc370#17 (consolidate the two try() wrappers), which touches the
same file and should not be settled twice.

Done when

  • The count above is explained rather than assumed.
  • The MTT question is answered by measurement, not assumption.
  • A shape is chosen for both messages — including the @@@try.c one, which has
    no cheap option channel.
  • An implementing issue exists in libc370, and this one links to it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions