Skip to content

Update SwappableLock to support NET9+ Lock type - #1077

Merged
dwcullop merged 4 commits into
reactivemarbles:mainfrom
dwcullop:bugfix/swappable-lock-net9
Apr 17, 2026
Merged

Update SwappableLock to support NET9+ Lock type#1077
dwcullop merged 4 commits into
reactivemarbles:mainfrom
dwcullop:bugfix/swappable-lock-net9

Conversation

@dwcullop

@dwcullop dwcullop commented Apr 10, 2026

Copy link
Copy Markdown
Member

Problem

SwappableLock is a ref struct used internally by Filter.Dynamic to atomically swap between lock objects during filtered changeset processing. It originally only supported Monitor.Enter/Monitor.Exit on object gates.

.NET 9 introduced System.Threading.Lock: a dedicated, more efficient lock type that is non-reentrant and does not support Monitor.Enter. Code paths that use Lock on .NET 9 were incompatible with SwappableLock.

Approach

Instead of sprinkling #if NET9_0_OR_GREATER throughout every method (mixing Lock and object state in the same struct), we split SwappableLock into two complete, independent implementations behind a single top-level #if:

  • NET9+: Uses System.Threading.Lock directly. Simpler (no _hasLock bool needed, Lock tracks its own state).
  • Pre-NET9: Uses Monitor.Enter/Exit on object gates. Unchanged behavior from the original.

Each implementation is self-contained and readable without mental #if gymnastics.

Changes

  • Internal/SwappableLock.cs - Two complete implementations behind top-level #if. NET9 version uses Lock.
  • Cache/Internal/Filter.Dynamic.cs - Gate properties return Lock on NET9, object otherwise. Dedicated Lock fields on NET9.
  • Tests/Internal/SwappableLockFixture.cs - New. 7 tests covering CreateAndEnter, Dispose (release + idempotent), SwapTo (basic + chained), uninitialized throws, and Dispose after swap.

Zero behavioral change on pre-.NET 9 targets.

Add Lock overloads for SwappableLock.SwapTo and constructor to support
the new System.Threading.Lock type on .NET 9+. Uses #if NET9_0_OR_GREATER
conditional compilation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dwcullop dwcullop changed the title Refactor SwappableLock to support NET9+ Lock type Update SwappableLock to support NET9+ Lock type Apr 10, 2026
@dwcullop
dwcullop requested review from JakenVeina and Copilot April 10, 2026 14:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds .NET 9+ System.Threading.Lock support to SwappableLock so ObservableCache can atomically swap between synchronization primitives without forcing object/Monitor locks on newer runtimes.

Changes:

  • Added CreateAndEnter(Lock gate) factory for NET9+.
  • Added SwapTo(Lock gate) overload and updated SwapTo(object gate) to correctly release whichever gate type is currently held.
  • Updated Dispose() to release a held Lock on NET9+.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/DynamicData/Internal/SwappableLock.cs Outdated
Comment thread src/DynamicData/Internal/SwappableLock.cs Outdated
Comment thread src/DynamicData/Internal/SwappableLock.cs Outdated
Comment thread src/DynamicData/Internal/SwappableLock.cs Outdated
… tests

Replace #if spaghetti throughout every method with two complete,
independent implementations behind a single top-level #if NET9_0_OR_GREATER.

NET9: uses System.Threading.Lock directly (simpler, no _hasLock bool needed).
Pre-NET9: uses Monitor.Enter/Exit on object gates (unchanged behavior).

Filter.Dynamic: gates upgraded to Lock on NET9 for consistency.

Added SwappableLockFixture with 7 tests covering CreateAndEnter, Dispose
(release + idempotent), SwapTo (basic + chained), uninitialized throws,
and Dispose after swap.
@dwcullop
dwcullop enabled auto-merge (squash) April 13, 2026 17:27
@dwcullop
dwcullop merged commit b0d4c70 into reactivemarbles:main Apr 17, 2026
1 of 2 checks passed
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators May 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants