Skip to content

Bug: folder-tree-dispatcher-thread-affinity #420

Closed

Description

@drmoisan
  • Work Mode: full-bug

Summary

The shared Outlook folder-tree service can invoke WpfDispatcherYield from a dispatcher-free worker after EmailDataMiner starts a cold cache build through Task.Run. Dispatcher.Yield then raises InvalidOperationException, and a fallback that only uses Task.Yield would not restore the required Outlook STA traversal contract.

Environment

  • OS/version: Windows with Outlook VSTO runtime
  • Application: TaskMaster Outlook add-in
  • Trigger: Cold folder-tree cache build or refresh that reaches a deadline-controlled yield point
  • Data source or fixture: Outlook folder hierarchy through the live COM adapter

Steps to Reproduce

  1. Start the email-mining path while the shared folder-tree cache requires a build or refresh.
  2. EmailDataMiner.MineEmails invokes ExtractOlFolderChunks through Task.Run.
  3. The folder-tree reader reaches WpfDispatcherYield.YieldAsync after the deadline clock requests a yield.

Expected Behavior

Live Outlook COM traversal and cooperative yielding remain on the captured Outlook STA dispatcher, and the caller receives a folder snapshot without a thread-affinity exception.

Actual Behavior

WpfDispatcherYield invokes Dispatcher.Yield(DispatcherPriority.Background) on a worker thread that has no current dispatcher. WPF raises InvalidOperationException indicating that the calling thread does not have a current Dispatcher.

Logs / Screenshots

  • Captured stack evidence reviewed
  • Snippet: EmailDataMiner -> OutlookFolderTreeService -> FolderTreeSnapshotBuilder -> OutlookFolderHierarchyReader -> WpfDispatcherYield -> Dispatcher.Yield

Impact / Severity

  • Blocker
  • High
  • Medium
  • Low

Source

From: docs/features/potential/2026-08-04-folder-tree-dispatcher-thread-affinity.md

Activity

  1. drmoisan commented on Aug 8, 2026

    @drmoisan
    OwnerAuthor

    Delivered by #422 (merged 2026-08-07, merge commit 0d85748) and the follow-up #423 (merged 2026-08-07, merge commit fb32b92). Neither PR body used a closing keyword, so this issue was not auto-closed.

    Verified against origin/main:

    • UtilitiesCS/OutlookObjects/Folder/WpfDispatcherYield.cs no longer calls Dispatcher.Yield(). It resolves a dispatcher explicitly as Dispatcher.FromThread(Thread.CurrentThread) ?? UtilitiesCS.UiThread.Dispatcher, posts a background-priority InvokeAsync, and throws InvalidOperationException with an actionable message when no dispatcher is available.
    • Folder-tree service composition, cold builds, refreshes, and notification cleanup are marshaled through the captured Outlook STA dispatcher, so the thread-first resolution preserves the STA traversal contract rather than falling back to the process-global dispatcher.
    • Regression coverage exists at WpfDispatcherYieldTests.YieldAsync_WithoutDispatcher_RemainsStrict and OutlookFolderTreeServiceConcurrencyTests.GetSnapshotAsync_WorkerOriginatedColdBuild_UsesCapturedStaDispatcher.

    Expected behavior in this issue (traversal and yielding remain on the captured Outlook STA dispatcher, no thread-affinity exception) is satisfied.

    Closing as delivered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions