From 2edee2855c6c22aa6938fc6671e17e30d63bc61d Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Thu, 25 Dec 2025 00:40:31 +0000 Subject: [PATCH] WritePriorityMutex: Fix rare case of dropped read waiter wakes The Race: 1. A Reader sets `READ_WAITER_BIT` (Bit 15) and sleeps on the High 16 bits (`Futex+2`). 2. Writer A unlocks. It clears `READ_WAITER_BIT` (in Low 16 bits) and `WRITE_OWNED` (in High 16 bits). 3. Writer B immediately steals the lock. It sets `WRITE_OWNED` but preserves the now-cleared `READ_WAITER_BIT`. 4. The Reader, checking `Futex+2`, sees `WRITE_OWNED` is set. Since it cannot see that Bit 15 was unset (as it is watching High 16 bits), it assumes its wait signal is still valid and sleeps. 5. Writer B unlocks. It sees no `READ_WAITER_BIT` and wakes nobody. Deadlock. The Fix: Move `READ_WAITER_BIT` to Bit 30 (High 16 bits). Now, when Writer A clears the flag, the High 16 bits change value which will prevent the wait from occurring within WaitForAddress --- FEXCore/Source/Utils/WritePriorityMutex.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/FEXCore/Source/Utils/WritePriorityMutex.h b/FEXCore/Source/Utils/WritePriorityMutex.h index 0b363ac8a0..29dbc641b5 100644 --- a/FEXCore/Source/Utils/WritePriorityMutex.h +++ b/FEXCore/Source/Utils/WritePriorityMutex.h @@ -353,14 +353,14 @@ class Mutex final { } constexpr static uint32_t WRITE_OWNED_BIT = 1U << 31; - constexpr static uint32_t READ_WAITER_BIT = 1U << 15; + constexpr static uint32_t READ_WAITER_BIT = 1U << 30; constexpr static uint32_t WRITE_WAITER_OFFSET = 16; constexpr static uint32_t WRITE_WAITER_INCREMENT = 1U << WRITE_WAITER_OFFSET; constexpr static uint32_t READ_OWNER_INCREMENT = 1; // Count masks - constexpr static uint32_t WRITE_WAITER_COUNT_MASK = 0x7FFFU << WRITE_WAITER_OFFSET; - constexpr static uint32_t READ_OWNER_COUNT_MASK = 0x7FFFU; + constexpr static uint32_t WRITE_WAITER_COUNT_MASK = 0x3FFFU << WRITE_WAITER_OFFSET; + constexpr static uint32_t READ_OWNER_COUNT_MASK = 0xFFFFU; // Independent futex bit-set masks. // Wait for readers to drain. @@ -373,9 +373,9 @@ class Mutex final { // Layout: // Bits[31]: Write-lock bit. - // Bits[30:16]: Write-waiter count. - // Bits[15]: Read-waiter bit. - // Bits[14:0]: Read-owner count. + // Bits[30]: Read-waiter bit. + // Bits[29:16]: Write-waiter count. + // Bits[15:0]: Read-owner count. uint32_t Futex {}; }; } // namespace FEXCore::Utils::WritePriorityMutex