Skip to content

[blazor][wasm] optimize away locks in hot render code#65900

Closed
Copilot wants to merge 3 commits intomainfrom
copilot/optimize-performance-blazor-wasm
Closed

[blazor][wasm] optimize away locks in hot render code#65900
Copilot wants to merge 3 commits intomainfrom
copilot/optimize-performance-blazor-wasm

Conversation

Copy link
Contributor

Copilot AI commented Mar 22, 2026

Eliminate lock/Monitor overhead on single-threaded WASM (browser) targets where no thread contention is possible, using OperatingSystem.IsBrowser() as a trimmer-recognized feature switch.

Description

On single-threaded WASM, lock/Monitor.Enter/Monitor.Exit are pure overhead — no thread contention exists. OperatingSystem.IsBrowser() is a trimmer-recognized feature switch, so guarding behind it lets the trimmer strip the synchronization code entirely from browser builds.

Changes

  • New type: SingleThreadedLockScope (src/Components/Components/src/Rendering/SingleThreadedLockScope.cs) — a ref struct with pattern-based disposal that conditionally acquires a Monitor lock only when !OperatingSystem.IsBrowser(). Zero-cost on WASM after trimming.
  • Renderer.cs — Replaced lock (_lockObject) with using (new SingleThreadedLockScope(_lockObject)) in 3 sites within Dispose(bool) and DisposeAsync().
  • RendererSynchronizationContext.cs — Replaced lock (_lock) with using (new SingleThreadedLockScope(_lock)) in 5 sites: InvokeAsync(Action), InvokeAsync<TResult>, Post, Send, and SendIfQuiescedOrElsePost.
  • TODO comment — Added // TODO use RuntimeFeature.IsMultithreadingSupported referencing [API] RuntimeFeature.IsMultithreadingSupported runtime#124603, to migrate to a more precise guard once the API ships (which will also handle multithreaded WASM correctly).

Copilot AI and others added 2 commits March 22, 2026 20:42
…leThreadedLockScope

Replace lock statements with SingleThreadedLockScope in Renderer.cs and RendererSynchronizationContext.cs. On browser targets (single-threaded WASM), OperatingSystem.IsBrowser() is a trimmer-recognized feature switch, allowing the trimmer to strip all Monitor.Enter/Exit calls since they are pure overhead without multithreading.

Co-authored-by: pavelsavara <271576+pavelsavara@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/aspnetcore/sessions/0c886500-ff3d-4c5b-a6a9-49a7898252b6
…on-nullable

Co-authored-by: pavelsavara <271576+pavelsavara@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/aspnetcore/sessions/0c886500-ff3d-4c5b-a6a9-49a7898252b6
…leThreadedLockScope

Co-authored-by: pavelsavara <271576+pavelsavara@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/aspnetcore/sessions/da00e8e5-acb2-479f-9133-424eaf82bd7f
Copilot AI requested a review from pavelsavara March 22, 2026 20:59
@pavelsavara pavelsavara marked this pull request as ready for review March 22, 2026 23:46
@pavelsavara pavelsavara requested a review from a team as a code owner March 22, 2026 23:46
Copilot AI review requested due to automatic review settings March 22, 2026 23:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a SingleThreadedLockScope helper intended to elide Monitor/lock overhead on single-threaded WebAssembly (browser) builds, and switches key renderer/sync-context critical sections from lock to this new scope.

Changes:

  • Add SingleThreadedLockScope ref struct that conditionally avoids taking a monitor lock on browser targets.
  • Replace several lock (...) { ... } blocks with using (new SingleThreadedLockScope(...)) { ... } in renderer synchronization and disposal paths.
  • Add a TODO note to eventually use a multithreading-aware runtime feature check.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/Components/Components/src/Rendering/SingleThreadedLockScope.cs Adds a lock-scope abstraction that can become a no-op on browser targets for trimming/perf.
src/Components/Components/src/Rendering/RendererSynchronizationContext.cs Replaces lock (_lock) with SingleThreadedLockScope to reduce overhead on WASM.
src/Components/Components/src/RenderTree/Renderer.cs Replaces lock (_lockObject) with SingleThreadedLockScope in dispose-related thread-safety paths.

@pavelsavara
Copy link
Member

@copilot update the PR description to match the change

Copy link
Contributor Author

Copilot AI commented Mar 23, 2026

@copilot update the PR description to match the change

Updated the PR description to reflect the full scope of changes: new SingleThreadedLockScope type, lock replacements in Renderer.cs (3 sites) and RendererSynchronizationContext.cs (5 sites), and the TODO comment.

@pavelsavara pavelsavara changed the title Optimize lock primitives for single-threaded WASM via trimmer-friendly guards [blazor][wasm] optimize away locks in hot render code Mar 23, 2026
@pavelsavara pavelsavara marked this pull request as draft March 23, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants