Skip to content

[External] Complete an asynchronous shutdown after the dispatcher of the Revit thread shuts down - #139

Draft
Nice3point wants to merge 1 commit into
developfrom
external/138-pump-shutdown-without-dispatcher
Draft

Nice3point wants to merge 1 commit into
developfrom
external/138-pump-shutdown-without-dispatcher

Conversation

@Nice3point

Copy link
Copy Markdown
Owner

Summary of the Pull Request

What is this about:

Completes OnShutdownAsync of an asynchronous external application after Revit shuts down the WPF dispatcher of its thread.

Description:

What changed

  • AsyncExternalApplication and AsyncExternalCommand wait for their task through one internal MessagePump.Run.
  • The wait depends on the state of the dispatcher of the calling thread:
Dispatcher at the call Before After
Running Pumps a DispatcherFrame Unchanged
Shutdown posted and not yet executed The frame exits before the task completes, and the blocking wait deadlocks The frame is created with exitWhenRequested: false and continues until the task completes
Shut down Dispatcher.PushFrame throws InvalidOperationException The continuations run on the calling thread
  • For a dispatcher that has shut down, the pump installs a SynchronizationContext that queues the continuations, and restores the previous context when the task completes.
  • During that wait, the pump dispatches the window messages of the thread with MsgWaitForMultipleObjectsEx and PeekMessage.
  • A WM_QUIT message removed during the wait is posted again with its exit code.
  • The remarks of OnShutdownAsync, the changelog, and the revit-toolkit-internals skill describe both states of the dispatcher.

Where to look

  • Revit shuts down the dispatcher itself: UIFramework.MainWindow.MainWindow_Unloaded calls Dispatcher.BeginInvokeShutdown. In five exits of Revit 2026, the shutdown had finished before ApplicationClosing.
  • A blocking wait was the alternative for a dispatcher that has shut down. The Revit thread has a WindowsFormsSynchronizationContext, which delivers a continuation as a window message, and a blocking wait never receives it.
  • The pump dispatches window messages with a raw message loop, without the message preprocessing of WPF. A queue of continuations without that loop was the alternative. It never completes a shutdown that awaits a callback delivered as a window message.
  • The frame ignores Dispatcher.ExitAllFrames() as well as the shutdown request. The dispatcher finishes its shutdown when the frame exits.
  • The queue context throws NotSupportedException for a Send from another thread, and never runs a callback posted after the asynchronous method completes.
  • The changelog opens the 2027.0.5 section. The build settings still state 2027.0.4.

How verified

  • Build: the library on Release.R20, Release.R24, and Release.R27, and the tests on Debug.R24, Debug.R26, and Debug.R27, zero errors and no new warnings.
  • Revit 2027: the whole test project, 144 passed.
  • Revit 2026 and Revit 2024: AsyncExternalApplicationTests, 9 passed.
  • With the previous wait restored, 8 of the 9 tests fail on Revit 2026: the six tests of a dispatcher that has shut down throw the exception of the issue, and the two tests of a posted shutdown time out.
  • A probe in an ApplicationClosing handler of Revit 2026 ran the same wait after the dispatcher shut down: the previous wait threw the exception of the issue, and the pump completed with the continuations on the Revit thread.
  • Not covered: an exit of Revit with an add-in built on this branch, and a test run on Revit 2020 to 2023 and 2025.

Open questions

  • A WM_QUIT message that arrives during the frame of a running dispatcher ends the frame before the task completes, and the wait after it blocks. No session showed this case, and the change leaves it as it is.

Quality Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings

@Nice3point Nice3point added bug 🐛 An unexpected issue that highlights incorrect behavior external commands 🚪 ExternalCommand, ExternalApplication, and external events labels Nov 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug 🐛 An unexpected issue that highlights incorrect behavior external commands 🚪 ExternalCommand, ExternalApplication, and external events

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The shutdown of an async external application throws after the dispatcher of the Revit thread shut down

1 participant