CNA-BUG-050: Guide message-box and keyboard-input results cannot be waited on: GamerServicesDispatcher::Update pumps nothing and their AsyncWaitHandle is signalled before completion

CNA snapshot 009d40f5  ·  Known Issues › Current bugs  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

The dispatcher's Update advances nothing and UpdateAsync only reports whether Initialize has run, and the Guide results that stay pending expose an already-signalled wait handle, so a pump loop on a pending Guide result spins forever and WaitOne returns before End can succeed.

Identifier
CNA-BUG-050
Category
Bug
Subsystem
Networking & gamer services
Status
Narrowed (partially fixed; describes only what survives)
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Severity
Low (a triage suggestion, not a project priority)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
GamerServicesDispatcher::Update(), GamerServicesDispatcher::UpdateAsync(); getAsyncWaitHandleProperty() and getIsCompletedProperty() of the IAsyncResult returned by Guide::BeginShowMessageBox and Guide::BeginShowKeyboardInput

Expected behaviour

The IAsyncResult contract: AsyncWaitHandle is signalled when the operation completes, so waiting on it and then calling End* yields the result. XNA's GamerServicesDispatcher.Update advances pending gamer-services work, and the synchronous-wrapper pattern CNA inherited from FNA (while (!result->IsCompleted) UpdateAsync();) relies on something advancing it.

Actual behaviour at TARGET

  • GamerServicesDispatcher.cpp: Update() is empty and UpdateAsync() returns isInitialized_, false before Initialize and true forever after it. The hang the original report described (network sessions and achievements) is fixed, because those actions now complete at construction (see the evidence). What survives are the two Guide families, the only Begin* results in the namespace that are still pending when Begin returns:
  • a message box completes only when the game's own Draw calls RenderPendingMessageBoxEXT and a mouse click lands on a button (or through SimulateMessageBoxClickEXT); a keyboard request completes when Enter arrives through TextInputEXT from the engine's event pump, or is cancelled through the render hook;
  • neither is advanced by Update, UpdateAsync or GamerServicesComponent, so while (!r->getIsCompletedProperty()) GamerServicesDispatcher::UpdateAsync(); on the game thread spins forever: the frame that would draw the box or pump the events never comes;
  • Guide.cpp's GuideAction constructs its EventWaitHandle signalled (asyncWaitHandle_(true, ManualReset)) and nothing ever resets or sets it, so WaitOne() returns at once while IsCompleted is false, and EndShowMessageBox then throws InvalidOperationException ("The message box has not been answered yet ...") and EndShowKeyboardInput throws InvalidOperationException ("The keyboard input has not been confirmed yet ...").

Source locations

Evidence

Checked by reading at 009d40f5; not executed. Fixed half, with positive evidence: the NetworkSessionAction constructor sets isCompleted_(true) with a signalled handle (NetworkSession.cpp), SignedInGamer::BeginGetAchievements marks its action complete, and the leaderboard and profile actions complete at construction, so no synchronous wrapper at TARGET waits on the dispatcher; GamerServicesDispatcherHangRegressionTest.cpp runs gamerservices_dispatcher_harness.cpp in a separate process under a ten-second watchdog (POSIX only; compiled out on Windows, Emscripten, Android and iOS). The pre-signalled wait handle was found while re-verifying this entry; the EventWaitHandle(bool initialState, EventResetMode) constructor stores initialState as the signalled state in a sibling Sharp Runtime checkout (next at 41b918c9, not pinned by TARGET). The Guide contract page documents the render-hook requirement but not that a dispatcher loop or WaitOne cannot work.

Focused reproduction

// Illustrative; not compiled or run for this entry.
using namespace Microsoft::Xna::Framework::GamerServices;
System::IAsyncResult* r = Guide::BeginShowMessageBox(
    "Quit?", "Unsaved progress will be lost.", {"Quit", "Cancel"}, 1,
    MessageBoxIcon::None, System::AsyncCallback{}, std::any{});
r->getAsyncWaitHandleProperty().WaitOne();    // returns at once: constructed signalled
auto choice = Guide::EndShowMessageBox(r);    // throws InvalidOperationException (not answered)
// Pumping instead never ends on the game thread:
// while (!r->getIsCompletedProperty()) GamerServicesDispatcher::UpdateAsync();

Current tests

GamerServicesServiceTests.cpp covers pending state, completion through the simulation helpers and injected text input, callbacks, re-entrancy and End-before-completion throwing; no test reads the wait handle of a Guide result or waits through the dispatcher. The dispatcher harness covers session creation and achievements (plus two initialisation modes), not the Guide.

Regression test

A test that begins a message box and asserts that a zero-timeout wait on its handle reports not signalled until SimulateMessageBoxClickEXT, and signalled afterwards; the same for a keyboard request completed by an injected Enter. For the dispatcher, either document Update/UpdateAsync as unable to complete Guide operations, or give them something observable to drive, with a harness case like the existing ones.

Blast radius

Code that waits for a Guide message box or keyboard input synchronously: by pumping the dispatcher while polling IsCompleted (FNA's wrapper pattern) or with WaitOne() followed by End. Callback-driven code and code that polls IsCompleted across frames while drawing the dialog, the pattern CNA documents, are unaffected, as are network, achievement, leaderboard and profile calls.

Workaround

Poll IsCompleted once per frame, call RenderPendingMessageBoxEXT/RenderPendingKeyboardInputEXT from Draw, and call End only when IsCompleted is true, or use the completion callback.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Known issues
Bug index