Repository navigation
Allow Dispatcher to be converted to a TaskScheduler - #21032
Conversation
|
You can test this PR using the following package version. |
|
Won't it be cheaper/easier to copy-paste https://github.com/dotnet/runtime/blob/51cfb0e382532e43500216c755d8bd8e1a8f371d/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs#L536-L602, wire it directly to Dispatcher::Post (or other API) rather than synchronization context and keep a lazy-crated reference in Dispatcher's field? |
This is the class which calls the internal method I mention in the PR description. |
|
Why do we need to call the internal method? It's called by TaskScheduler.TryExecuteTask: https://github.com/dotnet/runtime/blob/5dd24c0265d6682025aa4a2678776b7dc1200b15/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskScheduler.cs#L398-L406 |
|
Notes from the API review meeting: While the feature itself is good, we'd prefer explicit methods over implicit operators, especially since we need overloads.
|
|
API changes made!
|
Dispatcher to be converted to a TaskScheduler
|
The PR needs to be rebased onto the main branch for the tests to pass, and your branch is protected. |
|
You can test this PR using the following package version. |
This PR addresses as longstanding irritation with the boilerplate code required to execute continuation tasks on Avalonia's UI thread.
What is the current behaviour?
To ensure that a continuation task executes on the UI thread, you must execute
TaskScheduler.FromCurrentSynchronizationContexton the UI thread, and pass the returned object toContinueWith.This isn't difficult, but is annoying and unintuitive.
Now that Avalonia permits multiple threads to have dispatchers, the situation is more confusing than ever because you can no longer store the scheduler object in a static property and access this everywhere. You instead need each object tied to a dispatcher to convert
Dispatcher.Currentto a scheduler by itself.If you didn't create the scheduler when your object was constructed then to you probably have to post the
TaskScheduler.FromCurrentSynchronizationContextcall to the dispatcher and wait for it to execute there!What is the updated/expected behavior with this PR?
Avalonia.Dispatchercan now be converted toTaskScheduler, letting us write simple and intuitive code like this:This will work no matter what thread it's executed on.
Because the dispatcher is being converted to a different type, there is no change to its API. This avoids confusion between Avalonia's invocation methods and those of
TaskScheduler.How was the solution implemented (if it's not obvious)?
Unfortunately Microsoft don't make this job easy.
TaskScheduleris an abstract class that you can implement yourself, but if you try then you will find that you can't execute continuation tasks. The method you need to call to do this isinternaland exposed only via the object returned byTaskScheduler.FromCurrentSynchronizationContext...which ONLY uses the current thread's context.So we have to change
SynchronizationContextto be the target dispatcher, call the method, then resetSynchronizationContextback to its original value. Luckily this is a per-thread field and there are no side-effects to this switching.Breaking changes
None.
Obsoletions / Deprecations
None.