Skip to content

DYN-10409 Fix SynchronizationLockException crash on Save As in C3D#17071

Merged
jasonstratton merged 3 commits into
DynamoDS:masterfrom
jasonstratton:DYN-10409-C3D-Save-File-Crash
Apr 30, 2026
Merged

DYN-10409 Fix SynchronizationLockException crash on Save As in C3D#17071
jasonstratton merged 3 commits into
DynamoDS:masterfrom
jasonstratton:DYN-10409-C3D-Save-File-Crash

Conversation

@jasonstratton

@jasonstratton jasonstratton commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

Purpose

Fixes DYN-10409: Dynamo for Civil 3D crashes with an unhandled SynchronizationLockException inside WPF's CommandManager.FindCommandBinding after performing a Save As to a new .dyn file.

Root cause: DelegateCommand.RaiseCanExecuteChanged fired CanExecuteChanged directly on whatever thread called it. When the Dynamo scheduler thread completes graph execution (triggered by SetPeriodicEvaluation after the Save As workspace reload), WPF's CommandManager assumes CanExecuteChanged is always raised on the UI thread — raising it from a background thread corrupts the internal Monitor lock state, causing Monitor.Exit to throw with SynchronizationLockException.

Fix: Capture Dispatcher.CurrentDispatcher at DelegateCommand construction time (commands are always constructed on the UI thread). RaiseCanExecuteChanged now checks _dispatcher.CheckAccess() and marshals via BeginInvoke when called from a non-UI thread. UI-thread callers continue to invoke synchronously.

Declarations

Check these if you believe they are true

Release Notes

Fixed a crash (SynchronizationLockException in WPF's CommandManager) that occurred in Dynamo for Civil 3D after performing a Save As to a new file while running in Automatic evaluation mode.

Reviewers

@DynamoDS/eidos

Additional notes: The bug has been latent since DelegateCommand was introduced in January 2015 but surfaces reliably under conditions now common in C3D: Automatic run mode, Save As to a cloud-synced path (OneDrive), and the searchbar_separate_thread feature flag enabled. Regression tests added in DelegateCommandTests.cs using DynamoTestUIBase + DispatcherUtil.DoEventsLoop.

FYIs

Notes can be found here: https://git.autodesk.com/strattj/DynaNotes/blob/main/DYN-10409/README.md

Copilot AI review requested due to automatic review settings April 22, 2026 19:27

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the ticket for this pull request: https://jira.autodesk.com/browse/DYN-10409

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a WPF threading crash (DYN-10409) by ensuring DelegateCommand.RaiseCanExecuteChanged raises CanExecuteChanged on the UI dispatcher, and adds regression coverage plus agent-skill documentation updates.

Changes:

  • Marshal CanExecuteChanged to the captured WPF dispatcher when raised from background threads.
  • Add DelegateCommand threading regression tests covering background-thread, UI-thread, and concurrent raises.
  • Add two new agent skills (dynamo-build-tests, dynamo-run-tests) and reference them from agent docs.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/DynamoCoreWpf/Utilities/DelegateCommand.cs Dispatches CanExecuteChanged onto the dispatcher to avoid cross-thread WPF CommandManager crashes.
test/DynamoCoreWpfTests/DelegateCommandTests.cs Adds regression tests validating thread marshalling and concurrency behavior for RaiseCanExecuteChanged.
AGENTS.md Documents the newly added skills in the repo-level agent guidance.
.agents/skills/dynamo-run-tests/SKILL.md Adds a canonical workflow for running NUnit tests via dotnet test with repo-specific constraints.
.agents/skills/dynamo-build-tests/SKILL.md Adds a canonical workflow for building test projects reliably (MSBuild-first) in this repo.
.agents/README.md Updates the cross-tool parity matrix and skill list to include the new skills.

Comment thread src/DynamoCoreWpf/Utilities/DelegateCommand.cs Outdated
Comment thread test/DynamoCoreWpfTests/DelegateCommandTests.cs
Comment thread test/DynamoCoreWpfTests/DelegateCommandTests.cs Outdated
Comment thread .agents/skills/dynamo-build-tests/SKILL.md Outdated
jasonstratton and others added 3 commits April 22, 2026 15:54
DelegateCommand.RaiseCanExecuteChanged fired CanExecuteChanged directly
on whatever thread called it. When invoked from the Dynamo scheduler
thread (e.g. after graph execution triggered by a Save As workspace
reload), WPF's CommandManager.FindCommandBinding attempted to exit a
Monitor it never entered, crashing with SynchronizationLockException.

Marshal the event to the UI thread via Dispatcher.BeginInvoke when
called from a non-UI thread. UI-thread callers continue to invoke
synchronously. Adds regression tests in DelegateCommandTests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion.Current

Application.Current is null in DynamoTestUIBase tests (no WPF Application is
created), causing RaiseCanExecuteChanged to fall through to the direct-invoke
branch and fire on the background thread. Capturing Dispatcher.CurrentDispatcher
at DelegateCommand construction time is more robust: commands are always created
on the UI thread, so the captured dispatcher is always correct regardless of
whether Application.Current exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Re-read CanExecuteChanged inside BeginInvoke lambda rather than
  capturing a snapshot before posting, so unsubscriptions that happen
  in the window between scheduling and execution are respected.
- Pass explicit timeoutSeconds: 5 to DoEventsLoop in both regression
  tests so failures surface in seconds rather than stalling the suite
  for the 60-second default timeout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jasonstratton jasonstratton force-pushed the DYN-10409-C3D-Save-File-Crash branch from 12a1083 to d9d1ff6 Compare April 22, 2026 19:55
@sonarqubecloud

Copy link
Copy Markdown

@jasonstratton jasonstratton requested a review from a team April 22, 2026 22:00

@RobertGlobant20 RobertGlobant20 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, your comment about "The crash is a race condition and is not deterministically reproducible", then is possible to test it? or were you able to reproduce the crash and test your fix?

@jasonstratton

Copy link
Copy Markdown
Contributor Author

I was unable to test it myself. That's why the DynaNotes indicates "Suggested" steps to reproduce. 😆

@RobertGlobant20

Copy link
Copy Markdown
Contributor

ok then you just need to update your branch to see if Dynamo Selfserve pass

@jasonstratton

Copy link
Copy Markdown
Contributor Author

Whoops! Thought I had rerun SelfServe previously, but did not. Rerunning it now:
https://c007.cloudbees-ci.autodesk.com/job/DYNCI/job/Dynamo/job/DynamoSelfServe/job/pullRequestValidation/163/

@jasonstratton

Copy link
Copy Markdown
Contributor Author

ok then you just need to update your branch to see if Dynamo Selfserve pass

Is it better to update the branch or to rerun the failing job?
What do you do to update the branch? Push another commit?

@jasonstratton

Copy link
Copy Markdown
Contributor Author

Dynamo Selfserve passed

Merging now

@jasonstratton jasonstratton merged commit aac78b7 into DynamoDS:master Apr 30, 2026
30 of 31 checks passed
@jasonstratton jasonstratton added this to the 4.1.1 milestone May 8, 2026
@jasonstratton

Copy link
Copy Markdown
Contributor Author

/cherrypck

@jasonstratton

Copy link
Copy Markdown
Contributor Author

/cherrypick

@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown

Successfully created backport PR for RC4.1.1_master:

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