# DF-5840 — lwkt_rele() never wakes the td_refs pollers (tdreap / lwpwait2)

Info-grade hardening finding; verification SKIPPED per the audit contract
(Low/Info hardening findings with no memory-safety consequence).

## What

lwkt_rele() (sys/kern/lwkt_thread.c:502-507) decrements td_refs but never
issues wakeup(td), while both drain sites POLL instead of being woken:

  * lwkt_exit()  :1708-1711   `tsleep(td, 0, "tdreap", 1)`  — 1-tick poll
  * lwp_wait()   kern_exit.c:857-859  `tsleep(td, 0, "lwpwait2", 1)` — poll,
                   returns 0 to force the caller to retry lwp_wait()

Every thread teardown behind a held reference is therefore delayed by up to
one full tick after the last lwkt_rele(), and the exiting/reaping thread
burns a wakeup-per-tick cycle in the meantime.  Correctness is preserved
(the polls do drain); this is latency + needless scheduler load only —
under a ref-heavy workload (e.g. `ps -auxH` storms holding td refs from
kern_proc.c:1817 concurrent with kthread churn) exit latency inflates by
up to 10 ms per poll iteration at hz=100.

## Recommended fix

wake the pollers at drain: in lwkt_rele(), after the decrement, if
td_refs == 0 issue wakeup(td) (the wchan both pollers sleep on):

```diff
 void
 lwkt_rele(thread_t td)
 {
     KKASSERT(td->td_refs > 0);
     atomic_add_int(&td->td_refs, -1);
+    if (td->td_refs == 0)
+        wakeup(td);
 }
```

(wakeup on an empty channel is harmless; both pollers sleep on td itself
as the ident.)

## Verification

Not executed: no security impact to demonstrate (no corruption, no leak,
no achievable DoS beyond sub-tick teardown latency).  status: untested.
