Skip to content

Async fence completion does not unregister native waits (3.2.0 and main) #936

Open

Description

@andrewsil1

Description

The asynchronous fence-completion implementation registers a native wait but
does not unregister it before closing the waited-on event and freeing the
callback context. This appears to violate the Win32 registered-wait lifetime
contract.

This report is based only on the following publicly available upstream source:

Reproduction Steps

This is a source-level API-lifetime finding, with inspection steps rather than
a standalone runtime reproducer:

  1. Open GraphicsDevice.Execute.cs in either linked revision.
  2. Inspect WaitForFenceAsync. It calls RegisterWaitForSingleObject with flags
    zero and stores the returned registration handle only in a local variable.
  3. Inspect CallbackContext and WaitForSingleObjectCallbackForWaitForFenceAsync.
    The registration handle is not retained in the context. The callback closes
    EventHandle and frees its context/GCHandles without calling UnregisterWait
    or UnregisterWaitEx.
  4. Compare that lifecycle with the Microsoft documentation linked below.

Expected Behavior

The registered wait should be explicitly unregistered before releasing objects
whose lifetime depends on it. Cleanup must also handle a callback that races
the return from registration, and avoid blocking unregistration from inside
the callback itself.

Actual Behavior

The registration handle is discarded. No explicit native wait cancellation is
performed in the inspected completion path before the event/context are freed.

Microsoft's RegisterWaitForSingleObject documentation requires explicit wait
unregistration, including for WT_EXECUTEONLYONCE waits. It also states that
closing a handle while its wait is still pending is undefined behavior.
Consequently, changing the registration to one-shot by itself would not satisfy
the documented cleanup contract.

System Info

  • Affected released source: ComputeSharp v3.2.0, commit
    9a7c9e0.
  • Same implementation present in inspected main revision
    40dbe40.
  • Finding concerns Win32 wait ownership rather than GPU-specific shader behavior.

Additional Context

The latter documents nonblocking cancellation, ERROR_IO_PENDING when callbacks
are still outstanding, and the deadlock risk of blocking cancellation from the
callback itself. A fix should retain the registration handle, coordinate its
publication with completion, cancel the wait explicitly, and release its event
and context only once native lifetime requirements have been satisfied.

This report does not attribute any particular GPU failure to this defect.

Activity

  1. andrewsil1 commented on Sep 17, 2026

    @andrewsil1
    Author

    (Human here): FWIW, the bug description was generated by Codex, which deliberately rewrote itself to avoid publishing personal project details into this repo. However, it was discovered during the process of writing a ComputeSharp application that was leaking tens of thousands of handles over the course of a single math-heavy run of the app, as they were not being unregistered per the contract.

  2. rickbrew commented on Sep 17, 2026

    @rickbrew
    Collaborator

    Maybe you could get Claude to fix it? I don't mean that to be snarky, I'm just pretty sure it could fix it in a few minutes and you could submit the PR and then @Sergio0694 could merge it pretty quick

    (and then he could also review my PR #932 ... hint hint 😂)

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions