Skip to content

Fix #19: server-lifecycle & state-lock hardening#25

Merged
TheRealDinghyDog merged 2 commits into
opencode-conversionfrom
fix/issue-19-lifecycle
Jul 10, 2026
Merged

Fix #19: server-lifecycle & state-lock hardening#25
TheRealDinghyDog merged 2 commits into
opencode-conversionfrom
fix/issue-19-lifecycle

Conversation

@TheRealDinghyDog

Copy link
Copy Markdown
Owner

Closes #19.

Server-lifecycle & state-lock robustness.

  • SessionEnd teardown deadline (Med). teardownServerSession previously acquired the server lock with no acquisition timeout, so under contention it could exceed the ~5s SessionEnd hook budget and be killed mid-teardown. It now accepts a lock-acquire timeout and returns {skipped, reason:"lock-timeout", diagnostic} on contention; the hook passes a 3s server-lock + 500ms state-lock budget, logs the diagnostic, and skips clearServerSession on timeout. ensureServer also fails safely when a bounded acquisition times out (found via an internal review pass).
  • Event-loop stall on state-lock contention (Med). Added withStateLockAsync / updateStateAsync that poll with awaitable sleep instead of Atomics.wait, and migrated the async paths (SessionEnd job cleanup; enqueue/spawn bookkeeping, terminal-index sync, cancel read, in-flight cancellation in the companion) to them, so contention no longer freezes concurrent async work. The synchronous lock keeps its stale-takeover recovery — lockAcquireTimeoutMs is opt-in, with no premature default.
  • Undead EPERM leases (Low). processIsAlive now reports EPERM as "unconfirmed" and isLeaseActive requires a confirmed-alive owner, so a reused foreign pid can't hold teardown hostage for the 6h TTL. Plugin processes are same-user, so legitimate leases are unaffected.
  • Non-atomic server.json (Low). saveServerSession now uses the atomic temp-file + rename writer.

Verification: npm test — 79 passed, 0 failed, 0 skipped (localhost tests included). Regression tests cover bounded teardown diagnostics, sync stale-lock recovery beyond the former deadline, async-lock event-loop yielding, EPERM lease expiry, and atomic server.json.

🤖 Generated with Claude Code

TheRealDinghyDog and others added 2 commits July 10, 2026 06:26
- SessionEnd teardown is now bounded: teardownServerSession takes a lock-acquire
  timeout and returns {skipped, reason:"lock-timeout", diagnostic} on
  contention; the SessionEnd hook passes a 3s server-lock + 500ms state-lock
  budget, logs the diagnostic, and skips clearing state on timeout so it can't be
  killed mid-teardown by the hook host. ensureServer also fails safely when a
  bounded acquisition times out.
- State lock adds async APIs (withStateLockAsync / updateStateAsync) that yield
  the event loop via awaitable polling instead of Atomics.wait. The async
  companion paths (enqueue/spawn bookkeeping, terminal-index sync, cancel read,
  in-flight cancellation) and the SessionEnd job cleanup now use them. The
  synchronous lock keeps stale-takeover recovery (no premature default timeout;
  lockAcquireTimeoutMs is opt-in).
- EPERM is treated as "unconfirmed" (not alive) for lease liveness, so a reused
  foreign pid can no longer hold server teardown hostage for the 6h TTL; the TTL
  still covers legitimate long sessions.
- server.json is written atomically (temp-file + rename), reusing the state
  writer.

npm test: 79 passed, 0 failed, 0 skipped (localhost tests included).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sue-19-lifecycle

# Conflicts:
#	tests/runtime.test.mjs
@TheRealDinghyDog
TheRealDinghyDog merged commit 4bdc1c5 into opencode-conversion Jul 10, 2026
1 check passed
@TheRealDinghyDog
TheRealDinghyDog deleted the fix/issue-19-lifecycle branch July 11, 2026 00:55
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