Repository navigation
feat: add main-thread sequencers for MAUI, WinUI and WinForms - #225
Merged
Merged
Conversation
Give MAUI, WinUI and WinForms a shared main-thread sequencer, matching DispatcherSequencer.Main on WPF, so consumers such as ReactiveUI no longer resolve the UI dispatcher themselves. Each is added to both the ISequencer and the System.Reactive IScheduler flavours. MauiDispatcherSequencer.Main binds once to the running application's dispatcher, from any thread. Until the application exists it uses the calling thread's dispatcher. It throws when neither exists, and never falls back to the thread pool. MauiDispatcherSequencer.Current returns the calling thread's dispatcher sequencer, cached per thread. DispatcherQueueSequencer.Main binds to the first dispatcher queue it sees on a UI thread. A read from a thread without a dispatcher queue throws, and nothing is cached. DispatcherQueueSequencer.Current returns the calling thread's dispatcher queue sequencer, cached per thread. ControlSequencer.Main creates its hidden control only on an STA thread, so the handle belongs to a thread that can run a message loop. A read from any other thread throws instead of binding to a thread that never pumps messages. None of these properties creates a dispatcher.
ChrisPulman
approved these changes
Sep 24, 2026
glennawatson
enabled auto-merge (squash)
September 24, 2026 05:52
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #225 +/- ##
=======================================
Coverage 99.78% 99.78%
=======================================
Files 785 785
Lines 24792 24850 +58
Branches 2920 2946 +26
=======================================
+ Hits 24738 24796 +58
Misses 54 54 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.



Summary
MAUI, WinUI and WinForms each get a shared main-thread sequencer, like
DispatcherSequencer.Mainon WPF.MauiDispatcherSequencer.Mainand.Currentare new.Mainbinds once to the running application's dispatcher, from any thread. Before the application exists it uses the calling thread's dispatcher, and it throws when neither exists.Currentreturns the calling thread's dispatcher sequencer.DispatcherQueueSequencer.Mainand.Currentare new for WinUI.Mainbinds to the first dispatcher queue it sees on a UI thread. After that every thread gets the same sequencer.Currentreturns the calling thread's dispatcher queue sequencer.ControlSequencer.Mainis new for WinForms. On first read it creates a hidden control whose handle belongs to the calling thread. It does this only on an STA thread, and throws on any other thread.ISequencerpackages and the System.ReactiveISchedulerpackages get the same members.Why
Today ReactiveUI finds the UI dispatcher itself on these platforms, and each lookup can bind to the wrong thread.
Lazythat caches its exception. One read off the UI thread breaks the scheduler for the rest of the process.Breaking changes
None. Every change adds a member.
How this was verified
Tests cover binding, caching per thread, and the throw-without-caching rule for each new property.
Notes for the reviewer
Start with
MauiDispatcherSequencer. The WinUI and WinForms files follow the same shape.DispatcherSequenceron WPF: no property creates a dispatcher, and a failed lookup is never cached.MauiDispatcherSequencer.Mainreads the application's dispatcher fromIPlatformApplication.Current.Services. That keeps the package onMicrosoft.Maui.Coreand avoids a dependency onMicrosoft.Maui.Controls. MAUI resolves that dispatcher on the UI thread while it starts the application.DispatcherQueueSequencer.Mainhas to be read first on a UI thread.ControlSequencer.Mainuses that check. It still supports the usual pattern: read it inProgram.MainbeforeApplication.Run.Reactivefiles and the PublicAPI baselines are mechanical copies of theISequencerchanges.ReactiveUI.Primitives.MauigainsInternalsVisibleTofor its test project, like the Reactive project already has.Checklist