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
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 andUpdateAsync()returnsisInitialized_, 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
RenderPendingMessageBoxEXTand a mouse click lands on a button (or throughSimulateMessageBoxClickEXT); a keyboard request completes when Enter arrives throughTextInputEXTfrom the engine's event pump, or is cancelled through the render hook; - neither is advanced by Update,
UpdateAsyncorGamerServicesComponent, 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'sGuideActionconstructs itsEventWaitHandlesignalled (asyncWaitHandle_(true, ManualReset)) and nothing ever resets or sets it, soWaitOne()returns at once whileIsCompletedis false, andEndShowMessageBoxthen throwsInvalidOperationException("The message box has not been answered yet ...") andEndShowKeyboardInputthrowsInvalidOperationException("The keyboard input has not been confirmed yet ...").
Source locations
modules/gamer-services/src/Xna/GamerServicesDispatcher.cpp— GamerServicesDispatcher::Update is empty; UpdateAsync returns isInitialized_modules/gamer-services/src/Xna/Guide.cpp— GuideAction constructs its wait handle signalled; CompletePendingMessageBox and CompletePendingKeyboardInput; EndShowMessageBox and EndShowKeyboardInput throw while pendingmodules/gamer-services/include/Microsoft/Xna/Framework/GamerServices/Guide.hpp— BeginShowMessageBox documents completion only through RenderPendingMessageBoxEXT or SimulateMessageBoxClickEXTmodules/gamer-services/src/Xna/GamerServicesComponent.cpp— GamerServicesComponent::Update forwards to the empty dispatcher Updatemodules/net/src/Xna/NetworkSession.cpp— NetworkSessionAction is constructed complete (the fixed half)modules/gamer-services/src/Xna/SignedInGamer.cpp— BeginGetAchievements marks its action complete (the fixed half)modules/net/tests/CNA/Internal/Net/GamerServicesDispatcherHangRegressionTest.cpp— out-of-process regression cases for the fixed half
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.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.