Skip to content

Add stateful/combine disposable factories and an internal TakeUntil#23

Open
glennawatson wants to merge 3 commits into
mainfrom
AddDisposables
Open

Add stateful/combine disposable factories and an internal TakeUntil#23
glennawatson wants to merge 3 commits into
mainfrom
AddDisposables

Conversation

@glennawatson
Copy link
Copy Markdown
Contributor

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 until other emits; other completing without a value does not stop the source.

Validation (perf / alloc / thread-safety, in line with the codebase)

  • Thread-safety: verified sound — Interlocked.Exchange one-shot _stopped + a Lock gate serialize the terminal/OnNext callbacks, and MultipleDisposable.Add disposes-after-dispose so the synchronous-completion case is handled.
  • Dropped SerialDisposable: it conflicted with the System.Reactive name and reimplemented the lock-free replaceable-disposable with its own private DisposedMarker. The existing public SwapDisposable already provides this under a non-conflicting name, backed by the shared DisposableSlotHelper — one implementation.
  • Lightened TakeUntil: replaced Signal.Create(observer => …) (captured closure) + delegate-based Subscribe (3 method-group delegates + a lambda per subscription) with a dedicated TakeUntilSignal<T,TOther> holding its sources plus nested source/cancel IObserver classes — zero closure/delegate allocations, matching the FlatMap/Chain coordinator pattern. Behavior and thread-safety unchanged (the 3 ObservableMixinsTests still pass).
  • API snapshots: completed net9/net10 (were missing Combine/Create<TState>); net8 no longer lists SerialDisposable.

Tests

ObservableMixinsTests covers the three TakeUntil behaviors (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.

ChrisPulman and others added 3 commits June 3, 2026 16:45
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>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 3, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
66.2% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 3, 2026

Codecov Report

❌ Patch coverage is 64.40678% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.83%. Comparing base (ad00bbd) to head (f01ea5d).

Files with missing lines Patch % Lines
src/ReactiveUI.Primitives/ObservableMixins.cs 74.50% 7 Missing and 6 partials ⚠️
...tives/Disposables/StateActionDisposable{TState}.cs 0.00% 5 Missing ⚠️
...rc/ReactiveUI.Primitives/Disposables/Disposable.cs 0.00% 3 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

2 participants