Skip to content

[Vsintegration] Fix code model races while a solution closes - #2125

Merged
RobertvanderHulst merged 2 commits into
X-Sharp:devfrom
hpetriffer:fix/codemodel-close-race
Oct 27, 2026
Merged

RobertvanderHulst merged 2 commits into
X-Sharp:devfrom
hpetriffer:fix/codemodel-close-race

Conversation

@hpetriffer

Copy link
Copy Markdown
Contributor

Follow-up to #2124. While XSolution.Close() closes the code model database on the UI thread, background threads (CPS design-time builds, the ModelWalker) keep using it, and several threads read the same assemblies at once. The log then fills with No connection associated with this command, Database is not open and NullReferenceExceptions (AssemblyReader.AddType, OnTargetDesignTimeBuildChanged, FindType).

Changes

  • XDatabase: all access to the in memory connection is serialized by a fixed lock object instead of oConn, which CloseDatabase sets to NULL. Every method checks IsDbOpen again once it holds the lock (CHECKIFOPEN inside BEGIN LOCK), and CloseDatabase closes the connection under that lock. The backup still locks only around BackupDatabase; the VACUUM stays outside.
  • XAssembly.Read() runs under a lock per assembly and does not read again when another thread has just read the unchanged file. When the database is closed, file time and size are kept in memory so type lookups stop re-reading the assembly.
  • SystemTypeController.LoadAssembly: one lock per file name. Threads asking for the same assembly at the same time each created and read their own XAssembly (up to 30 reads of one file).
  • XSharpProjectAdapter (CPS): no reference resolution, file changes or new target models while the solution closes or after it has closed; CPS keeps delivering design-time builds until the projects are unloaded.
  • XProject.RootNamespace no longer throws after Close() cleared the project node while a background parse still resolves types.

Test

VS 2022 experimental instance, RadixWfSDK.slnx, new database, closed 60 s after loading, devenv pinned to the E-cores (the race only shows while VS is still busy at close), same setup for both builds:

dev (#2124) this PR
[ERR] between "Closing solution" and "Shutdown VS" 75 0
Close (Closing solution → Shutdown VS) 16.2 s 2.6 s

On the P-cores both builds close in 2.4–5 s; assembly reads during the load dropped from 656–3354 to 566–595.

Not addressed (pre-existing, same in both builds): FOREIGN KEY failures in SaveProjectAssemblyReferences during the load, caused by the orphan cleanup deleting assembly rows another project has just read but not linked yet.

🤖 Generated with Claude Code

hpetriffer and others added 2 commits October 26, 2026 18:24
While XSolution.Close() closed the database on the UI thread, background
threads (CPS design-time builds, the ModelWalker) kept using it, and
several threads read the same assemblies at the same time. In the logs
this showed up as "No connection associated with this command",
"Database is not open" and NullReferenceExceptions in
AssemblyReader.AddType, OnTargetDesignTimeBuildChanged and FindType.

- XDatabase: all access to the in memory connection is serialized by a
  fixed lock object instead of oConn, which CloseDatabase sets to NULL.
  Every method checks IsDbOpen again once it holds the lock, and
  CloseDatabase closes the connection under that lock. The backup locks
  only around BackupDatabase as before; the VACUUM stays outside.
- XAssembly.Read() runs under a lock per assembly and does not read
  again when another thread has just read the unchanged file. When the
  database is closed, the file time and size are kept in memory, so type
  lookups do not read the assembly over and over.
- SystemTypeController.LoadAssembly: one lock per file name. Threads
  that asked for the same assembly at the same time each created (and
  read) their own XAssembly; the logs showed 30 reads of one file.
- XSharpProjectAdapter: no reference resolution, file changes or new
  target models while the solution closes or after it has closed. CPS
  keeps delivering design-time builds until the projects are unloaded.
- XProject.RootNamespace no longer fails after Close() cleared the
  project node while a background parse still resolves types.

Measured in the VS 2022 experimental instance with RadixWfSDK.slnx,
closed 60 s after loading, with a new database and devenv pinned to the
E-cores (the race only shows while VS is still busy): 75 errors between
"Closing solution" and "Shutdown VS" before, 0 after; the close took
16.2 s before and 2.6 s after. Assembly reads during the load went from
656-3354 to 566-595.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RobertvanderHulst
RobertvanderHulst merged commit 0039f6a into X-Sharp:dev Oct 27, 2026
1 check passed
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.

2 participants