Skip to content

PowerManager SystemSuspendStatus unreliable due to race condition #5224

Open
@BernhardMarconato

Description

@BernhardMarconato

Describe the bug

The PowerManager SystemSuspendStatus property together with the SystemSuspendStatusChanged event do not work properly due to a race condition.

Because there are no event arguments, the new SystemSuspendStatus must be queried from within the event handler separately. But in the time between the first event firing and querying the property in the event handler, another event may occur which would simply overwrite the old m_systemSuspendStatus value. The value of the first event would be lost, and two events having the same value from the second event.

This isn't even a special case, but happens all the time when waking up from standby. The two events are broadcasted after each other, BUT in separate threads. This means the second event does not wait for the first event handler to finish. The documentation of the Win32 API, states:

If the system wakes due to user activity (such as pressing the power button) or if the system detects user interaction at the physical console (such as mouse or keyboard input) after waking unattended, the system first broadcasts the PBT_APMRESUMEAUTOMATIC event, then it broadcasts the PBT_APMRESUMESUSPEND event. If the system wakes due to an external wake signal (remote wake), the system broadcasts only the PBT_APMRESUMEAUTOMATIC event. The PBT_APMRESUMESUSPEND event is not sent.

With the WASDK API, it would be impossible to only handle the PBT_APMRESUMEAUTOMATIC event because of the race condition - you would sometimes read out the value from the latter event in both the first and second event (and sometimes not, if Windows was slow that time).

The only proper way to fix it is to replace the EventHandler<object> with a EventHandler<SystemSuspendStatus> or similar, to guarantee the correct value for the specific event.

Steps to reproduce the bug

  1. Add following code to your app
PowerManager.SystemSuspendStatusChanged += PowerManager_SystemSuspendStatusChanged;
private void PowerManager_SystemSuspendStatusChanged(object? sender, object e)
{
    var newStatus = PowerManager.SystemSuspendStatus;
    Debug.WriteLine($"{DateTime.Now:u} SystemSuspendStatusChanged: {newStatus} (Thread {Environment.CurrentManagedThreadId})");
}
  1. Run the app
  2. Put your computer in standby
  3. Resume your computer from standby
  4. Check the logs

Sometimes, it works:

2025-03-12 21:20:08Z SystemSuspendStatusChanged: Entering (Thread 5)
2025-03-12 21:20:19Z SystemSuspendStatusChanged: AutoResume (Thread 5)
2025-03-12 21:20:19Z SystemSuspendStatusChanged: ManualResume (Thread 6)

Most of the times, a race condition happens:

2025-03-12 21:21:12Z SystemSuspendStatusChanged: Entering (Thread 5)
2025-03-12 21:21:46Z SystemSuspendStatusChanged: ManualResume (Thread 5)
2025-03-12 21:21:47Z SystemSuspendStatusChanged: ManualResume (Thread 6)

Expected behavior

Expecting a consistent behavior of the API without race conditions: an event with AutoResume followed by another event with ManualResume after a standby wakeup.

Screenshots

No response

NuGet package version

Windows App SDK 1.6.6: 1.6.250228001

Packaging type

Packaged (MSIX)

Windows version

Windows 11 version 24H2 (22621, October 2024 Update)

IDE

Visual Studio 2022

Additional context

The documentation https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.windows.system.power.powermanager.systemsuspendstatuschanged?view=windows-app-sdk-1.6 is also very lacking and fails to explain the event sequence being sent.

It also fails to mention that the app can block the event handler for up to 2 seconds, delaying system suspend (as explained here). Or does that even work? When looking at the implementation, the event seems to be fired in a separate thread, making this non-functioning? https://github.com/microsoft/WindowsAppSDK/blob/main/dev/PowerNotifications/PowerNotifications.h#L266C26-L266C50

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions