Repository navigation
Add .NET 10 compatibility support - #524
Frank Shearar (frankshearar) wants to merge 22 commits into
Conversation
7de020b to
a635b5d
Compare
Upgrade xUnit to 2.6.2 and the Visual Studio adapter to 2.5.4. These releases include the fix for disabled collection parallelization with synchronous reporting: xunit/visualstudio.xunit#392
a635b5d to
b8df478
Compare
b8df478 to
03e62e5
Compare
Control WaitAsync completion, preserve WhenEach completion order, and separate Lock and Monitor state. Add regression coverage and align repository, probe, and pipeline SDK pins. Copilot-Session: f70e2526-22a3-48bf-b1be-454449bca15d
Update the three IL-diff hash baselines affected by the new WaitAsync, WhenEach, and Lock/Monitor regression tests. Local .NET 10 and .NET 8 comparisons match the Windows CI output. Copilot-Session: f70e2526-22a3-48bf-b1be-454449bca15d
Exercise locally built library and CLI packages in cross-platform sample CI. Use isolated package caches, require matching framework assets, and rewrite and test package-based probes with the installed tool. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 109deff7-3338-4d4a-ac06-6feb4b58fdaf
|
There are a few additional concurrency APIs/contracts worth covering before declaring .NET 10 support. Comparing the .NET 8, 9, and 10 reference surfaces with the replacements in this PR shows these gaps (including .NET 9 additions that .NET 10 applications can use):
The generic Interlocked contract change also illustrates why the API audit needs to include changed constraints/semantics, not just newly named methods. I would ask this PR to address the two atomic gaps and explicitly classify the barrier and wait-handle boundaries. I would not require modeling every new async library feature (async ZIP APIs, JSON-over-pipelines, async LINQ); those can follow the existing external-library/uncontrolled-operation policy. Similarly, the absence of wrappers for These are coverage findings from inspection, not yet reproduced runtime failures like the previously discussed |
|
Three additional review findings, separate from the runtime API coverage gaps: [P1] Preserve the first completion event in
|
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 109deff7-3338-4d4a-ac06-6feb4b58fdaf
Integrates upstream Monitor.SynchronizedBlock.ResetCache (microsoft#510) and Dependabot config (microsoft#520). [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/prepare] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
Snapshot of the approved plan (SHA256 6A284A21C7E09E4EC8AC4017D2AE05BB5817EA510F43FF458EFD2193F2C65117) guiding the seven review fixes. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/prepare] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
The shared controlled WaitAsync helper chose the cancellation task whenever the wait token was canceled by the time the helper ran, so a source that completed before the token was canceled was reported as canceled. The wait cancellation registration now samples the source state when the token is canceled and does not cancel the wait if a source that delivers its outcome synchronously has already completed. Sources that run continuations asynchronously keep racing a later cancellation, as the uncontrolled runtime does. Fast paths, token identity, registration cleanup, the finite-timeout model and the fuzzing/native paths are unchanged. Adds a native oracle and regressions for result, fault and source cancellation in both event orders, for generic and non-generic waits, with default and asynchronous source continuations, plus the reviewer's TaskCompletionSource reproducer in systematic, rewriting and fuzzing tests. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/fix-waitasync] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
The controlled array WaitAll, which the enumerable overload delegates to, checked the token only before pausing and resumed only when every task completed, so a cancellation requested after the wait started was reported as a deadlock. The controlled wait now also resumes when a cancelable token is canceled. The first of task completion and cancellation is recorded when the token is canceled, matching the uncontrolled wait: completion first returns or propagates task faults, a requested cancellation still takes precedence over canceled tasks unless a task faulted, and cancellation first throws for the wait token without completing pending tasks. Callers without a cancelable token, validation, empty inputs, uncontrolled-task detection, the timeout policy and fuzzing are unchanged. Adds a native oracle and regressions for array and enumerable overloads where the wait starts before cancellation, for result, fault and source cancellation in both event orders, an already-canceled token with completed and empty inputs, controlled completion with a cancelable token, and a genuine deadlock with a never-canceled token. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/fix-waitall] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
On .NET 9 and later, the runtime adds Interlocked.Exchange and CompareExchange for byte, sbyte, short and ushort, and accepts primitive and enum type arguments for the generic overloads. The small-integer calls were passed through without a scheduling point, and explicit primitive or enum generic calls were redirected to the class-constrained replacement, which produced unverifiable IL. The replacement now adds the eight small-integer overloads and removes the class constraint from the generic overloads on .NET 9 and later, exploring an interleaving and then delegating to the runtime so that it keeps its own type validation. Older targets keep the reference-only contract and do not reference the newer APIs. Tests cover old-value returns and successful and failed comparisons for all four types, lost updates that are found only through the new scheduling points, explicit generic calls for a reference type, a primitive and enums of different widths against the native outcome, native NotSupportedException parity for an unsupported value type, and the rewritten IL of all eight overloads and the explicit generic calls. The API gate now identifies generic parameters by position and arity, and rejects replacements with stricter generic constraints. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/fix-atomics] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
… test When a blocked uncontrolled WaitAll observes a canceled task and the wait token is canceled right after, it reports either the requested cancellation or an aggregate TaskCanceledException, depending on when the blocked wait wakes up. The native oracle assertion required the former and failed intermittently. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/classify-barriers] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
Systematic testing executes one controlled operation at a time and does not explore weak-memory reorderings. Volatile reads and writes and Interlocked operations add a scheduling point before their memory access, while barriers that do not access memory, such as Thread.MemoryBarrier and Interlocked.MemoryBarrier, have no replacement and are invoked natively. The .NET 10 Volatile.ReadBarrier and WriteBarrier follow the same policy: they never block, so they are not uncontrolled synchronization, and they only constrain reorderings that Coyote does not model. Documents the memory-ordering boundary in the binary rewriting docs and the .NET 10 compatibility assessment, classifies both barriers as pass-through with a reason in the runtime API gate, and adds tests that the rewritten IL keeps the barriers as runtime calls next to controlled volatile accesses, that volatile accesses are scheduling points, and that barriers are not. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/classify-barriers] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
An earlier edit in this remediation rewrote the file without its original UTF-8 byte order mark. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/named-handles] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
The name-bearing EventWaitHandle constructors created the native named system event and registered it as a process-local controlled resource, although other processes can share a named event, and the .NET 10 constructors that take NamedWaitHandleOptions were not intercepted at all. During systematic testing, every EventWaitHandle constructor with a non-empty name now reports a bug and throws NotSupportedException before the system event is created or opened. A null or empty name natively creates an unnamed, unshared event, so it stays controlled, including through the new NamedWaitHandleOptions overloads. Opening methods and Mutex and Semaphore stay reported as uncontrolled invocations, and native behavior outside interleaving testing is unchanged. Tests cover the native null and empty name contract, rejection of all four name-bearing constructors with a check that the named event does not exist afterwards on Windows, controlled unnamed events through every overload, opening methods including NamedWaitHandleOptions, and Mutex and Semaphore NamedWaitHandleOptions constructors. The API gate classifies the NamedWaitHandleOptions opening methods with a reason, and the binary rewriting docs and .NET 10 assessment document the boundary. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/named-handles] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
…cations The runtime API gate inspected a hand-written list of signatures and derived its classifications from the same list, so a new runtime overload never entered the gate. The gate now discovers, through reflection, every public method, accessor and constructor of explicitly audited families: Task wait, join, delay and RunSynchronously, Task<T>.WaitAsync, Monitor, SemaphoreSlim, Interlocked Increment, Exchange and CompareExchange, Volatile barriers, WaitHandle.WaitOne, Thread.Sleep, Lock, and EventWaitHandle, Mutex and Semaphore constructors and openers. Signatures capture constructor, static or instance form, generic arity and constraints, ref, out and in parameters, and return type. Every discovered member needs exactly one framework-specific classification: controlled with a compatible replacement, rejected by the uncontrolled invocation rewriting pass with a reason, or passed through with a reason. Unclassified, missing-replacement, overlapping, stale and empty-reason classifications fail the gate on .NET 10, 8, 6 and .NET Framework 4.6.2. Discovery found three gaps, now fixed: Monitor.TryEnter(object, int) and Task.Wait(TimeSpan, CancellationToken) had no controlled replacement and blocked natively, and the .NET Framework EventWaitHandle constructor that takes EventWaitHandleSecurity created a named event without being reported. Adds a fixture family with an extra overload run through the real discovery path, stale, overlap, reason and rewriting-mismatch regressions, and rewriting tests for the two new replacements. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/api-gate] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
Updates the expected IL diff hashes of Tests.Rewriting and Tests.BugFinding on .NET 10 and 8 for the tests added by the review remediation. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/api-gate] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
Describes how controlled Task.WaitAsync and Task.WaitAll order completion and cancellation in the binary rewriting docs, and appends a dated addendum to the .NET 10 compatibility assessment that summarizes the seven review outcomes while keeping the original assessment as historical evidence. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/validate] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
The packages ship net10.0, net8.0, net6.0 and netstandard2.0 assets, so programs that target .NET 9 resolve the net8.0 build, which has neither the small-integer Interlocked replacements nor the relaxed generic contract. The assessment said both applied on .NET 9 and later. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/review-diff] Copilot-Session: 65bb6a21-b626-4e36-b214-355fba112613
On Linux and macOS the .NET runtime rejects an EventWaitHandle created with an empty name with a PlatformNotSupportedException, whereas on Windows an empty name creates an unnamed event. The controlled constructors already pass empty names to the runtime, so they keep that native behavior, but the tests and docs assumed the Windows contract everywhere, which failed the Ubuntu CI job. The native oracle and the unnamed event tests now assert the platform-specific native contract, including that the non-Windows failure is the runtime's own exception rather than a Coyote rejection, and the docs describe both platforms. Refreshes the Tests.Rewriting IL diff hashes for the updated tests. [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/ci-green] Copilot-Session: 71190617-9b6c-4fe4-b5f6-7af5e3190a95
|
Frank the Clank says: the review findings from Akash Lal (@akashlal) in the API coverage comment and the correctness and gate comment are addressed in the commits below. Each was first reproduced as a failing test against the previous code where applicable. PR head is now
Also included: a merge of Not changed, per your guidance: async ZIP, JSON-over-pipelines and async LINQ stay under the existing external-library policy, and no [cdb:ri_mv1nmbnv_0000005_17bb2b0c07a4/respond] |
No description provided.