Skip to content

Fix races: relocate signal-cli tmp root, join bridge goroutines on Close#30

Merged
MaxGhenis merged 1 commit into
mainfrom
fix/signal-tmp-root-location
Jun 12, 2026
Merged

Fix races: relocate signal-cli tmp root, join bridge goroutines on Close#30
MaxGhenis merged 1 commit into
mainfrom
fix/signal-tmp-root-location

Conversation

@MaxGhenis

Copy link
Copy Markdown
Owner

Summary

The Go Race CI job on main failed after #28 (TestConnectEmitsSignalQRCodeAndStoresPairedAccount: "TempDir RemoveAll cleanup: directory not empty"). Investigating reproduced two distinct races:

  1. Run-tmp locationFix libsignal temp dir leak from signal-cli subprocess churn #28 put per-invocation temp dirs under the Signal config dir. Any teardown that removes that dir wholesale (production Unpairos.RemoveAll(configDir), t.TempDir() cleanup) races with a lingering invocation's MkdirTemp. The root now lives at os.TempDir()/openmessage-signal-cli-<uid> — app-owned, per-user, outside every wholesale-removal path. All Fix libsignal temp dir leak from signal-cli subprocess churn #28 guarantees hold (per-run dir removed on process exit, startup + periodic sweeps, legacy libsignal* reaping, OPENMESSAGES_SIGNAL_TMP_SWEEP=0 opt-out).
  2. Close without joinBridge.Close cancelled contexts but never waited for the link/receive/refresh/sweep/sync goroutines, so reusing package state after Close (tests swapping the runSignalCLI stub between -count runs; serve restarting a bridge) raced with loops still draining. Goroutines are now tracked in a WaitGroup; Close joins, and Unpair joins before RemoveAll(configDir) — a draining poll can still be writing WAL/recovery files inside it.

Verification

  • go test ./internal/signallive -race -run TestConnectEmitsSignalQRCode -count=30: all pass (previously ~1/20 failure)
  • go test ./internal/signallive -race -count=3, go vet ./..., full go test ./...: green

🤖 Generated with Claude Code

Two races surfaced by the Go Race CI job after #28:

- The per-run temp dirs lived under the Signal config dir, so removing
  that dir (Unpair's RemoveAll, t.TempDir cleanup) raced with a
  lingering invocation still creating its run dir inside and failed
  with "directory not empty". The run-tmp root now lives in an
  app-owned, per-uid directory under the system temp dir, which no
  teardown path removes wholesale.

- Bridge.Close cancelled background contexts but never waited, so
  package state reused after Close (tests swapping the runSignalCLI
  stub between runs, serve restarting a bridge) raced with loops still
  draining. Background goroutines are now tracked in a WaitGroup;
  Close joins them, and Unpair joins before removing the config dir a
  draining poll may still be writing WAL/recovery files into.

TestConnectEmitsSignalQRCodeAndStoresPairedAccount previously failed
under -race roughly 1 in 20 runs; 30 consecutive runs now pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit be70103 into main Jun 12, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant