Skip to content

Make subject termination permanent and fix zero-capacity replay - #69

Merged
twittemb merged 2 commits into
mainfrom
codex/fix-subject-terminal-and-replay
Oct 3, 2026
Merged

twittemb merged 2 commits into
mainfrom
codex/fix-subject-terminal-and-replay

Conversation

@twittemb

@twittemb twittemb commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Description

Repeated termination can give existing and late subject subscribers different outcomes. Values sent after termination can also change a current-value subject or repopulate cleared replay history, and replay capacity zero currently retains one historical value.

Make the first finish or failure permanent across all six subjects and ignore later values, current-value assignments, and termination signals. Keep the last accepted current value and cleared replay history. Both replay variants now retain no history at capacity zero while delivering every live value to existing subscribers.

Preserve atomic registration and FIFO delivery outside the subject lock. Concurrent sends can still return while another sender drains their delivery; channel delivery does not wait for application code to process the value. Document these contracts and map each subject guarantee to its regression coverage.

Related: the guarantee audit in issue #61.

The commits separate permanent termination from zero-capacity replay. No public signatures, dependency requirements, deployment targets, channel implementations, or delivery-queue mechanisms change.

Validation

Validated on Apple Swift 6.4 / macOS. The committed content matches the tested snapshot exactly.

  • Before the fixes, the added tests reproduced terminal-outcome disagreement, post-termination state changes and payload retention, and zero-capacity historical replay.
  • Added 12 tests, including 1,200 concurrent termination races across the three throwing subjects. Expanded paused-delivery regressions across all six subjects to cover sends after termination without losing earlier accepted values.
  • swift build -Xswiftc -suppress-warnings: passed.
  • swift test --filter 'Async.*Subject.*Tests' -Xswiftc -suppress-warnings: 81 tests passed, including registration, cancellation, ordering, queued delivery, termination, and lifetime coverage.
  • swift test --enable-code-coverage -Xswiftc -suppress-warnings: all 199 tests passed, zero failures.
  • git diff origin/main..HEAD --check: passed.

A Swift 5.8 toolchain was unavailable locally; minimum-toolchain validation was not run.

Checklist

  • this PR is based on the main branch and is up-to-date
  • the commits inside this PR have explicit commit messages
  • unit tests cover the new feature or the bug fix
  • the feature is documented in the README.md if it makes sense
  • the CHANGELOG is up-to-date

Ignore later values and terminal signals across all six subjects, preserve the first outcome for late consumers, and freeze stored state. Cover repeated and concurrent termination, paused delivery, and ignored-value lifetime; document the complete subject regression contract.
Retain no replay history when capacity is zero while delivering live values to existing subscribers. Cover late subscription and payload release for both replay variants and document the capacity contract.
@twittemb
twittemb merged commit 8c22107 into main Oct 3, 2026
2 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