Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make so elmish update and cmd loop can run on non-ui thread #552

Merged

Conversation

marner2
Copy link
Collaborator

@marner2 marner2 commented Jan 28, 2023

The basic implementation of this PR would be to invoke all calls to the underlying dispatch on one thread, then invoke all calls to UpdateViewModel on the UI thread associated with the window.

However, WPF expects events to raise NotifyPropertyChanged events before they exit, otherwise things like textboxes will "reset" following the disconnected update coming from the elmish update thread. This causes, for instance, the cursor to jump to a different location as you are typing. This means that we cannot simply invoke the dispatch calls coming from WPF in an async manner - we must block that call until the elmish update thread has processed that update and generated NotifyPropertyChanged events. This block is ok, because any dispatch calls from the non-ui thread will dispatch the UI update asyncronously.

One of the optimizations is to skip UpdateViewModel invocations if there is a pending UI update. This is ok, because calls to this function are transitive (skipping one in the middle still results in the same final state).

EDIT: Added a sample project that illustrates the threading capability and how to use it.

@TysonMN
Copy link
Member

TysonMN commented Jan 29, 2023

This means that we cannot simply invoke the dispatch calls coming from WPF in an async manner - we must block that call until the elmish update thread has processed that update and generated NotifyPropertyChanged events.

What is the advantage of doing work in another thread while the UI thread is blocked compared to doing that work on the UI thread instead of blocking?

@marner2
Copy link
Collaborator Author

marner2 commented Jan 30, 2023

@TysonMN

What is the advantage of doing work in another thread while the UI thread is blocked compared to doing that work on the UI thread instead of blocking?

If you are only updating the model from actions coming from the UI, there is none. However if you have subscriptions, you can guarantee that when you dispatch from a subscription, the update to the model always happens right away no matter what is happening on the UI thread. This has two potential advantages:

  • If the update function generates elmish commands, those commands will always get generated and processed immediately on this background thread even if the UI is locked for some reason (maybe overwhelming updates to the view model). In our project's case, this is important because we are using the model state to emit instructions to a machine as well as update the UI state. We don't want that to slow down if the UI is processing something.
  • If you get a bunch of UI updates "stacked up," you can safely drop the intermediate queue of updates to the UI since they are all bindings, and skipping an intermediate state will still leave the UI in the correct state (the transformation is stateless). I'm not doing that here, except that I drop updates while a UI update is pending.

There may be a gotcha with skipping updates and the captured model getting applied to the view model. Ideally we would always grab the latest model each time, so if we execute them "out of order" it doesn't result in an older one getting applied last (especially in the ui dispatch case).

@marner2 marner2 marked this pull request as draft March 28, 2023 19:37
@marner2 marner2 mentioned this pull request Mar 31, 2023
41 tasks
@marner2 marner2 marked this pull request as ready for review May 2, 2023 01:21
@marner2 marner2 force-pushed the feature/add_multithreading_capability branch from c4ff716 to 73bf4b9 Compare May 2, 2023 01:37
src/Elmish.WPF/WpfProgram.fs Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
src/Elmish.WPF/WpfProgram.fs Outdated Show resolved Hide resolved
marner2 and others added 3 commits May 4, 2023 18:43
Add comments

Co-authored-by: Lyndon Gingerich <84735110+LyndonGingerich@users.noreply.github.com>
@marner2 marner2 merged commit ee18ea6 into elmish:master May 5, 2023
@marner2 marner2 deleted the feature/add_multithreading_capability branch May 5, 2023 23:19
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.

3 participants