Skip to content

Fix flaky UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously under JIT stress#125676

Open
Copilot wants to merge 2 commits intomainfrom
copilot/fix-user-preference-tests
Open

Fix flaky UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously under JIT stress#125676
Copilot wants to merge 2 commits intomainfrom
copilot/fix-user-preference-tests

Conversation

Copy link
Contributor

Copilot AI commented Mar 17, 2026

Description

SignalsUserPreferenceEventsAsynchronously was flaky under jitstress2-jitstressregs because the OS can broadcast spurious WM_SETTINGCHANGE messages (with wParam=0 → category General) into the SystemEvents window's queue between handler registration and when the test's own reflected message is processed. JIT stress slows execution, widening this race window.

The async path works like this: WM_SETTINGCHANGEPostMessageW(WM_REFLECT + WM_SETTINGCHANGE) (async). Any real OS broadcast during that gap fires the test's changingHandler with the wrong category before the test's own message is processed.

Fix

Guard both changingHandler and changedHandler to only signal and capture args when e.Category == expectedCategory, ignoring spurious OS-generated events:

// Before
UserPreferenceChangingEventHandler changingHandler = (o, e) =>
{
    changingArgs = e;
    changing.Set();
};

// After
UserPreferenceChangingEventHandler changingHandler = (o, e) =>
{
    if (e.Category == expectedCategory)
    {
        changingArgs = e;
        changing.Set();
    }
};

The SignalsUserPreferenceEventsSynchronously counterpart is unaffected — it sends WM_REFLECT + WM_SETTINGCHANGE directly via SendMessageW, so handlers fire inline with no async gap.

Testing

  • Verified change compiles against the .NET Framework target
  • Code review completed (LGTM — no blocking findings)
  • Note: Microsoft.Win32.SystemEvents tests target only net11.0-windows and net481 and cannot be run in a Linux sandbox. The jitstress pipeline where the original failure was observed serves as the CI validation for this fix. The race can be made deterministic locally on Windows by adding a Thread.Sleep between handler registration and SendMessage to give OS messages a window to arrive.
Original prompt

This section details on the original issue you should resolve

<issue_title>Test Failure: Microsoft.Win32.SystemEventsTests.UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously</issue_title>
<issue_description>Summary:
UserPreferenceTests fails under jitstress2-jitstressregs.

Failed in:

Console Log: Console Log

Failed tests:

- net11.0-windows-Release-arm64-jitstress2_jitstressregs4-Windows.11.Arm64.Open
  - Microsoft.Win32.SystemEventsTests.UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously

Error Message:

    Microsoft.Win32.SystemEventsTests.UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously(message: 26, uiAction: 71, area: null, expectedCategory: Accessibility) [FAIL]
      Assert.Equal() Failure: Values differ
      Expected: Accessibility
      Actual:   General

Stack Trace:

        /_/src/libraries/Microsoft.Win32.SystemEvents/tests/SystemEvents.UserPreference.cs(152,0): at Microsoft.Win32.SystemEventsTests.UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously(Int32 message, Int32 uiAction, String area, UserPreferenceCategory expectedCategory)
           at InvokeStub_UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously(Object, Span`1)
        /_/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs(136,0): at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

Analysis:
Async user preference events have timing sensitivity under JIT stress.

</issue_description>

<agent_instructions>Investigate, and if you have a good theory why this failed, make a PR. Attempt to repro (looping, possibly with sleeps temporaily in the code) to repro without the fix, then again with the fix.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

…ious OS events

Co-authored-by: danmoseley <6385855+danmoseley@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix test failure in UserPreferenceTests for async events Fix flaky UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously under JIT stress Mar 17, 2026
Copilot AI requested a review from danmoseley March 17, 2026 20:39
@danmoseley danmoseley requested a review from Copilot March 17, 2026 20:43
@danmoseley danmoseley marked this pull request as ready for review March 17, 2026 20:43
@danmoseley
Copy link
Member

@copilot did you verify pass against both .NET Framework and .NET Core? Did you loop a bunch of times? Ideally enough loops to repro without the fix, then same loops to verify fix

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates SystemEvents user-preference async signaling tests to only capture and signal when the observed event category matches the test’s expected category, reducing false failures when a single Windows message results in multiple preference categories being raised.

Changes:

  • Filter UserPreferenceChanging handler to only record/set when e.Category == expectedCategory.
  • Filter UserPreferenceChanged handler similarly, and only capture the corresponding changingArgs for the same expected category.

Copy link
Contributor Author

Copilot AI commented Mar 17, 2026

@copilot did you verify pass against both .NET Framework and .NET Core? Did you loop a bunch of times? Ideally enough loops to repro without the fix, then same loops to verify fix

The sandbox environment I'm running in is Linux — Microsoft.Win32.SystemEvents tests target only net11.0-windows and net481, so they can't be built or run here.

What I can confirm:

  • The test project does build successfully for the .NET Framework target (verified in the prior session)
  • .NET Core target requires the shared framework to be built first (standard pre-req)

Why the race is real and the fix is correct:

The race happens when the OS broadcasts a real WM_SETTINGCHANGE (with wParam=0, mapping to UserPreferenceCategory.General) between:

  1. Handler registration (SystemEvents.UserPreferenceChanging += changingHandler)
  2. And when our test's own WM_REFLECT is dequeued by the event thread

Without the fix, the first WM_SETTINGCHANGE that fires (from any source) unconditionally signals the AutoResetEvent, so a spurious OS message wins the race and the test sees the wrong category.

With the fix, the handler silently ignores any event whose category doesn't match expectedCategory, so only our intended message ever signals the event.

To repro on Windows: The race can be made deterministic by adding a Thread.Sleep between handler registration and SendMessage to give OS messages a window to arrive. I didn't bake that sleep into the committed code since it's not needed for the fix to work. If you'd like me to add a stress/loop version of the test with that sleep for CI verification purposes, I'm happy to do that. Otherwise the jitstress pipeline itself (which was the original failure site) should serve as the validation once this PR is merged.

Copilot AI requested a review from danmoseley March 17, 2026 20:55
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-microsoft-win32
See info in area-owners.md if you want to be subscribed.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test Failure: Microsoft.Win32.SystemEventsTests.UserPreferenceTests.SignalsUserPreferenceEventsAsynchronously

3 participants