1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | internal bool32 Win32DoNextWorkQueueEntry(platform_work_queue *Queue) { bool32 WeShouldSleep = false; uint32 OriginalNextEntryToRead = Queue->NextEntryToRead; uint32 NewNextEntryToRead = (OriginalNextEntryToRead + 1) % ArrayCount(Queue->Entries); if(OriginalNextEntryToRead != Queue->NextEntryToWrite) { uint32 Index = InterlockedCompareExchange((LONG volatile *)&Queue->NextEntryToRead, NewNextEntryToRead, OriginalNextEntryToRead); // -> SPOT X <- if(Index == OriginalNextEntryToRead) { platform_work_queue_entry Entry = Queue->Entries[Index]; Entry.Callback(Queue, Entry.Data); InterlockedIncrement((LONG volatile *)&Queue->CompletionCount); } } else { WeShouldSleep = true; } return(WeShouldSleep); } |
At the -> SPOT X <- marked in the code, isn't there a possibility that the main thread will write to the queue at the given NewNextEntryToRead a new entry? Which would result in worker thread to pickup the newer entry, and the old entry will be lost. Now the code might work in the scenario it is used in HMH (considering we're waiting for all threads to finish every frame before we schedule new work), but other scenarios would end up having to deal with it.
Also would this fix it? I've moved reading of the Entry just before we do the exchange. Entry might be invalid at this point, but we test it's validity by the exchange, and only when it's confirmed we'd use it to do the work.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | internal bool32 Win32DoNextWorkQueueEntry(platform_work_queue *Queue) { bool32 WeShouldSleep = false; uint32 OriginalNextEntryToRead = Queue->NextEntryToRead; uint32 NewNextEntryToRead = (OriginalNextEntryToRead + 1) % ArrayCount(Queue->Entries); if(OriginalNextEntryToRead != Queue->NextEntryToWrite) { // Get the entry before we increment the Read head. // NOTE: Original post had NewNextEntryToRead here, but it's been fixed after a suggestion of mrmixer. We want the non-incremented index here. platform_work_queue_entry Entry = Queue->Entries[OriginalNextEntryToRead]; uint32 Index = InterlockedCompareExchange((LONG volatile *)&Queue->NextEntryToRead, NewNextEntryToRead, OriginalNextEntryToRead); if(Index == OriginalNextEntryToRead) { // We were the ones who updated the Read head, so we now have a valid Entry we can proceed with. Entry.Callback(Queue, Entry.Data); InterlockedIncrement((LONG volatile *)&Queue->CompletionCount); } } else { WeShouldSleep = true; } return(WeShouldSleep); } |