Skip to content

Concurrent shared compilations can fail releasing the client mutex on Linux #134043

Description

@JeremyKuhne

Description

After Csc and Vbc opted into MSBuild multithreaded task execution in dotnet/roslyn#84701, a dotnet/sdk file-based-app test can intermittently fail when -mt builds two project references in the same MSBuild process with shared compilation enabled:

Microsoft.CSharp.Core.targets(97,5): error MSB3883: Unexpected exception:
Microsoft.CSharp.Core.targets(97,5): error : Cannot release a lock that is not owned by the current thread.

The failure is tracked by dotnet/sdk#56238. It occurred on Ubuntu 22.04 x64 with .NET 11 SDK 11.0.100-ci; test history reported a 0.52% failure rate. The failing Helix log shows LibraryB completing while another compiler invocation fails.

Distinguishing trigger

The failing test creates a file-based app with two #:project references and runs:

dotnet build Program.cs -mt

It also sets MSBUILDUSESERVER=0, so the MSBuild server is disabled while Roslyn shared compilation remains enabled. In the same 108-test work item, the ordinary file-based builds passed; only MultiThreadedArgument_BuildsProjectReferencesInProcess(0, -mt) failed. Its -mt:false row passed immediately afterward.

dotnet/sdk#56239 proposes UseSharedCompilation=false for this one test to isolate its MSBuild in-process-node assertion. That is useful test isolation, but it is a mitigation rather than a fix for this failure.

Relevant implementation and history

BuildServerConnection.RunServerBuildRequestAsync serializes server startup with a named client mutex. Its tryConnectToServerAsync local function is deliberately non-async so WaitOne and ReleaseMutex execute on the same thread. However, Roslyn's current tests exercise requests sequentially and do not appear to cover concurrent calls sharing the same pipe/client mutex from one process.

This resembles:

The runtime synchronization correction associated with dotnet/dotnet#4088 is present in the runtime used by this failure, so this appears to be a recurrence exposed by the new concurrent compiler-task execution rather than simply a missing backflow.

The current failure also lacks Roslyn's WaitOne Id / Release Id diagnostic. tryConnectToServerAsync catches ApplicationException around mutex disposal, while the .NET 11 Unix implementation surfaced this ownership failure as InvalidOperationException in dotnet/dotnet#4088.

Requested investigation

  1. Add a Linux regression/stress test that invokes RunServerBuildRequestAsync concurrently with the same pipe name, or otherwise runs concurrent shared-compilation Csc tasks.
  2. Determine whether Roslyn is violating the mutex lifetime contract or whether this is a .NET 11 Unix named-mutex regression; transfer or split the runtime portion if needed.
  3. Preserve the acquisition/release thread IDs when this exception occurs on .NET 11 so a future occurrence is actionable.

Note

This report was prepared with GitHub Copilot assistance from the linked failure logs and source investigation.

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions