Add stateful/combine disposable factories and an internal TakeUntil#23
Open
glennawatson wants to merge 3 commits into
Open
Add stateful/combine disposable factories and an internal TakeUntil#23glennawatson wants to merge 3 commits into
glennawatson wants to merge 3 commits into
Conversation
Introduce SerialDisposable and StateActionDisposable<TState> to support replaceable disposables and stateful disposal semantics, both implemented with thread-safe Interlocked/Volatile usage. Extend Disposable with Create<TState> and Combine overloads to create stateful disposables and combine multiple disposables via MultipleDisposable. Add internal ObservableMixins.TakeUntil helper to forward values until another observable emits, using a gate and MultipleDisposable for coordinated completion/error handling.
Replace inline locking/subscription management in ObservableMixins.TakeUntil with a dedicated TakeUntilCoordinator<T> that serializes observer callbacks, manages subscriptions, and ensures single completion/error handling and proper disposal. Add ObservableMixinsTests to verify TakeUntil behavior when the other observable emits, completes, or errors. Update API approval file to reflect added Disposable helpers and SerialDisposable types.
…es work Validate the AddDisposables changes for perf/alloc/thread-safety in line with the codebase: - Drop the new SerialDisposable (it conflicted with the System.Reactive name and reimplemented the lock-free replaceable-disposable with a private DisposedMarker). The existing public SwapDisposable already provides it under a non-conflicting name, backed by the shared DisposableSlotHelper — one implementation. - Rewrite TakeUntil as a dedicated TakeUntilSignal<T,TOther> with nested source/ cancel IObserver classes instead of Signal.Create(closure) + delegate-based Subscribe, removing the per-subscription closure and delegate allocations while preserving the Interlocked one-shot + gate thread-safety. - Complete the API snapshots (net9/net10 were missing Combine/Create<TState>; net8 no longer lists SerialDisposable). Co-Authored-By: Chris Pulman <chris.pulman@yahoo.com>
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #23 +/- ##
==========================================
- Coverage 91.93% 91.83% -0.11%
==========================================
Files 407 409 +2
Lines 16019 16078 +59
Branches 2363 2372 +9
==========================================
+ Hits 14727 14765 +38
- Misses 971 986 +15
- Partials 321 327 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
ChrisPulman
approved these changes
Jun 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


What
Adds disposable conveniences and an internal
TakeUntil:Disposable.Create<TState>(state, action)— a stateful one-shot disposable (no captured closure).Disposable.Combine(first, second)/Combine(params IDisposable[]).ObservableMixins.TakeUntil<T,TOther>(internal) — forwards source values untilotheremits;othercompleting without a value does not stop the source.Validation (perf / alloc / thread-safety, in line with the codebase)
Interlocked.Exchangeone-shot_stopped+ aLockgate serialize the terminal/OnNextcallbacks, andMultipleDisposable.Adddisposes-after-dispose so the synchronous-completion case is handled.SerialDisposable: it conflicted with the System.Reactive name and reimplemented the lock-free replaceable-disposable with its own privateDisposedMarker. The existing publicSwapDisposablealready provides this under a non-conflicting name, backed by the sharedDisposableSlotHelper— one implementation.TakeUntil: replacedSignal.Create(observer => …)(captured closure) + delegate-basedSubscribe(3 method-group delegates + a lambda per subscription) with a dedicatedTakeUntilSignal<T,TOther>holding its sources plus nested source/cancelIObserverclasses — zero closure/delegate allocations, matching the FlatMap/Chain coordinator pattern. Behavior and thread-safety unchanged (the 3ObservableMixinsTestsstill pass).net9/net10(were missingCombine/Create<TState>);net8no longer listsSerialDisposable.Tests
ObservableMixinsTestscovers the threeTakeUntilbehaviors (other-emits-completes, other-completes-without-value-continues, other-errors-forwarded). Full suite green on net9; core builds clean on net8.0/net9.0/net462.