33
44#include " pch.h"
55#include " referenced_ptr.h"
6+ #include " XTaskQueuePriv.h"
67#include " TaskQueueP.h"
78#include " TaskQueueImpl.h"
8- #include " XTaskQueuePriv.h"
9-
10- #ifdef HC_UNITTEST_API
11- TestBarrier* g_testBarrier = nullptr ;
12- #endif
139
1410//
1511// ApiRefs tracks global refcounts for all APIs. It is used to identify memory leaks
@@ -1111,32 +1107,27 @@ bool TaskQueuePortImpl::ScheduleNextPendingCallback(
11111107 uint64_t noDueTime = UINT64_MAX;
11121108 if (m_timerDue.compare_exchange_strong (dueTime, noDueTime))
11131109 {
1114- #ifdef HC_UNITTEST_API
1115- // Test hook: two-phase barrier reproduces the timer race
1116- // that results in lost delayed task wakes.
1117- if (g_testBarrier != nullptr )
1118- {
1119- {
1120- std::lock_guard<std::mutex> lk (g_testBarrier->mtx );
1121- g_testBarrier->phase1_ready = true ;
1122- }
1123- g_testBarrier->cv .notify_all ();
1124-
1125- std::unique_lock<std::mutex> lk (g_testBarrier->mtx );
1126- g_testBarrier->cv .wait_for (lk, std::chrono::seconds (5 ),
1127- [&] { return g_testBarrier->phase2_ready ; });
1128- }
1129- #endif
11301110 // Bug fix: ScheduleNextPendingCallback timer race results
1131- // in lost delayed task wakes.
1132- //
1111+ // in lost delayed task wakes. Don't cancel the timer here
1112+ // as another scheduled callback could have been added.
11331113 // The CAS above is sufficient: the timer has already fired
11341114 // (call site 1: SubmitPendingCallback) or was already
11351115 // canceled (call site 2: CancelPendingEntries). A Cancel()
11361116 // here raced with concurrent QueueItem/Start calls on other
11371117 // threads, permanently stranding entries in m_pendingList.
11381118 // See VerifyDelayedCallbackTimerRaceOnManualQueue for full
1139- // analysis.
1119+ // analysis. The test hook here allows unit tests to verify
1120+ // there is no race.
1121+ m_attachedContexts.Visit ([&](ITaskQueuePortContext* portContext)
1122+ {
1123+ auto hooks = portContext->GetQueue ()->GetTestHooks ();
1124+ if (hooks != nullptr ) {
1125+ hooks->NextPendingCallbackScheduled (
1126+ portContext->GetType (),
1127+ dueTime,
1128+ noDueTime);
1129+ }
1130+ });
11401131 }
11411132 }
11421133
@@ -1277,14 +1268,14 @@ void TaskQueuePortImpl::SignalTerminations()
12771268 entry->portContext ->AddRef ();
12781269
12791270 entry->callback (entry->callbackContext );
1280-
1281- // Release portContext after callback completes
1282- entry->portContext ->Release ();
1283-
1271+
12841272 {
12851273 std::lock_guard<std::mutex> lock (m_terminationLock);
12861274 m_terminationList->free_node (address);
12871275 }
1276+
1277+ // Release portContext after callback completes
1278+ entry->portContext ->Release ();
12881279 delete entry;
12891280 }
12901281}
@@ -2349,3 +2340,17 @@ STDAPI_(bool) XTaskQueueUninitialize(
23492340
23502341 return ApiRefs::WaitZeroRefs (timeoutMilliseconds);
23512342}
2343+
2344+ // / <summary>
2345+ // / Sets or clears test hooks on a task queue.
2346+ // / </summary>
2347+ STDAPI XTaskQueueSetTestHooks (
2348+ _In_ XTaskQueueHandle queue,
2349+ _In_ XTaskQueueTestHooks* hooks
2350+ ) noexcept
2351+ {
2352+ referenced_ptr<ITaskQueue> aq (GetQueue (queue));
2353+ RETURN_HR_IF (E_GAMERUNTIME_INVALID_HANDLE, aq == nullptr );
2354+ aq->SetTestHooks (hooks);
2355+ return S_OK;
2356+ }
0 commit comments