@@ -234,7 +234,7 @@ bool GPUUploadManagerImpl::Page::ScheduleBufferUpdate(IBuffer*
234234
235235void GPUUploadManagerImpl::Page::ExecutePendingOps (IDeviceContext* pContext, Uint64 FenceValue)
236236{
237- VERIFY (DbgIsSealed (), " Page must be sealed before executing pending operations" );
237+ VERIFY (IsSealed (), " Page must be sealed before executing pending operations" );
238238 VERIFY (DbgGetWriterCount () == 0 , " All writers must finish before executing pending operations" );
239239
240240 if (m_pData != nullptr && !m_PersistentMapped)
@@ -284,7 +284,7 @@ void GPUUploadManagerImpl::Page::Reset(IDeviceContext* pContext)
284284
285285bool GPUUploadManagerImpl::Page::TryEnqueue ()
286286{
287- VERIFY (DbgIsSealed (), " Page must be sealed before it can be enqueued for execution" );
287+ VERIFY (IsSealed (), " Page must be sealed before it can be enqueued for execution" );
288288 VERIFY (DbgGetWriterCount () == 0 , " All writers must finish before the page can be enqueued" );
289289
290290 bool Expected = false ;
@@ -412,6 +412,25 @@ void GPUUploadManagerImpl::ScheduleBufferUpdate(IDeviceContext* pC
412412 continue ;
413413 }
414414
415+ // Prevent ABA-style bug when pages are reset and reused
416+ // * Thread T1 loads P0 as current, then gets descheduled before TryBeginWriting().
417+ // * Render thread swaps current to P1, seals P0, sees it has 0 ops, calls Reset(nullptr)
418+ // which clears SEALED_BIT, and pushes P0 to FreePages
419+ // * T1 resumes and calls P0->TryBeginWriting() and succeeds (because SEALED_BIT is now cleared).
420+ // * T1 schedules an update into a page that is not current and is simultaneously sitting in the free list,
421+ // potentially being popped/installed elsewhere.
422+ {
423+ Page* CurNow = m_pCurrentPage.load (std::memory_order_acquire);
424+ // Validate that the page is either:
425+ // * still current OR
426+ // * already sealed (meaning it got rotated after we began writing, which is fine because writer count protects it)
427+ if (P != CurNow && !P->IsSealed ())
428+ {
429+ Writer.EndWriting (); // Undo the writer count
430+ continue ; // Reload current and retry
431+ }
432+ }
433+
415434 const bool UpdateScheduled = Writer.ScheduleBufferUpdate (pDstBuffer, DstOffset, NumBytes, pSrcData, Callback, pCallbackData);
416435 if (Writer.EndWriting () == Page::WritingStatus::LastWriterSealed)
417436 {
@@ -492,7 +511,7 @@ bool GPUUploadManagerImpl::TryRotatePage(IDeviceContext* pContext, Page* Expecte
492511
493512bool GPUUploadManagerImpl::TryEnqueuePage (Page* P)
494513{
495- VERIFY_EXPR (P->DbgIsSealed ());
514+ VERIFY_EXPR (P->IsSealed ());
496515 if (P->TryEnqueue ())
497516 {
498517 if (P->GetNumPendingOps () > 0 )
0 commit comments