SIGN IN SIGN UP

fix(processtree): stop the exit manager racing on its stop channel (SUB-7847)

The cleanup loop read pt.exitCleanupStopChan in its select on every
iteration while stopExitManager wrote nil to the same field, neither side
synchronised. Every one of the seven data races the race detector reports
in pkg/processtree is this one field pair — exit_manager.go:41 write
against :67 read — and it failed five tests in the creator package.

Two further defects fell out of the same code, both reproduced rather than
inferred:

- Two concurrent Stop() calls could both pass the nil check and both reach
  the select's default branch, so both closed the channel: "panic: close
  of closed channel".
- A loop that read the nilled field rather than the closed one blocked
  forever on a nil-channel receive, killing that select arm, so the loop
  kept ticking and mutating the tree for the lifetime of the process.
  Which of the two happened was decided by the race itself.

The loop now takes the channel as an argument and never reads the field,
and both lifecycle transitions are held under a dedicated mutex. close()
is still the stop signal and nil is still the restartable "not running"
flag, so the observable contract does not move: TestStartStop and
TestExitManager_StartStop pass with their nil-after-stop assertions
untouched.

A dedicated mutex rather than pt.mutex, because the tree lock is held
across performExitCleanup and reusing it would couple shutdown to tree
contention.

Documents the lifecycle in docs/features/process-tree-exit-manager-
lifecycle.md, including one property worth knowing before writing tests
here: closing the channel does not preempt an in-flight iteration, and
when both select arms are ready the runtime chooses at random, so a
cleanup pass can still run after Stop() returns. The guarantee is prompt
termination, not zero further work.

Verified in the Linux container, since macOS cannot build this package:
go vet ./pkg/processtree/... && go test -race ./pkg/processtree/...
vet clean, 0 race reports, every package ok — down from 7 races and 5
failing tests on the same command at 8866b6c8.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Alon <alon@armosec.io>
A
Alon committed
21a4fed6f21df3b74d052a8a3cd7b17e86743198
Parent: 8866b6c