diff --git a/src/xenia/base/atomic.h b/src/xenia/base/atomic.h index 5ea64de82..cd2e3b2f0 100644 --- a/src/xenia/base/atomic.h +++ b/src/xenia/base/atomic.h @@ -66,6 +66,14 @@ inline bool atomic_cas(int64_t old_value, int64_t new_value, old_value) == old_value; } +// Atomic store with release semantics (for releasing locks) +inline void atomic_store_release(int32_t new_value, volatile int32_t* value) { + _InterlockedExchange(reinterpret_cast(value), new_value); +} +inline void atomic_store_release(uint32_t new_value, volatile uint32_t* value) { + _InterlockedExchange(reinterpret_cast(value), new_value); +} + #elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC inline int32_t atomic_inc(volatile int32_t* value) { @@ -85,10 +93,26 @@ inline int32_t atomic_xor(volatile int32_t* value, int32_t nv) { } inline int32_t atomic_exchange(int32_t new_value, volatile int32_t* value) { - return __sync_val_compare_and_swap(value, *value, new_value); + int32_t old_value; + do { + old_value = *value; + } while (!__sync_bool_compare_and_swap(value, old_value, new_value)); + return old_value; } inline int64_t atomic_exchange(int64_t new_value, volatile int64_t* value) { - return __sync_val_compare_and_swap(value, *value, new_value); + int64_t old_value; + do { + old_value = *value; + } while (!__sync_bool_compare_and_swap(value, old_value, new_value)); + return old_value; +} + +// Atomic store with release semantics (for releasing locks) +inline void atomic_store_release(int32_t new_value, volatile int32_t* value) { + __atomic_store_n(value, new_value, __ATOMIC_RELEASE); +} +inline void atomic_store_release(uint32_t new_value, volatile uint32_t* value) { + __atomic_store_n(value, new_value, __ATOMIC_RELEASE); } inline int32_t atomic_exchange_add(int32_t amount, volatile int32_t* value) { diff --git a/src/xenia/base/threading.h b/src/xenia/base/threading.h index d2d30998e..b7ec709af 100644 --- a/src/xenia/base/threading.h +++ b/src/xenia/base/threading.h @@ -83,8 +83,8 @@ class Fence { std::mutex mutex_; std::condition_variable cond_; // Use the highest bit (sign bit) as the signal flag and the rest to count - // waiting threads. - volatile state_t_ signal_state_; + // waiting threads. Protected by mutex_. + state_t_ signal_state_; }; // Returns the total number of logical processors in the host system. diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index b768c6872..1b2c988c1 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -546,7 +546,7 @@ class PosixCondition final : public PosixConditionBase { } std::weak_ptr wait_item_; std::function callback_; - volatile bool signal_; + bool signal_; // Protected by mutex_ const bool manual_reset_; }; @@ -820,6 +820,7 @@ class PosixCondition final : public PosixConditionBase { bool is_current_thread = pthread_self() == thread_; { + std::unique_lock lock(state_mutex_); if (out_previous_suspend_count) { *out_previous_suspend_count = suspend_count_; } @@ -908,8 +909,8 @@ class PosixCondition final : public PosixConditionBase { pthread_t thread_; bool signaled_; int exit_code_; - volatile State state_; - volatile uint32_t suspend_count_; + State state_; // Protected by state_mutex_ + uint32_t suspend_count_; // Protected by state_mutex_ mutable std::mutex state_mutex_; mutable std::mutex callback_mutex_; mutable std::condition_variable state_signal_; diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index f284f88bc..56c0b735d 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -1167,8 +1167,8 @@ DECLARE_XBOXKRNL_EXPORT3(KfAcquireSpinLock, kThreading, kImplemented, kBlocking, void xeKeKfReleaseSpinLock(PPCContext* ctx, X_KSPINLOCK* lock, uint32_t old_irql, bool change_irql) { assert_true(lock->prcb_of_owner == static_cast(ctx->r[13])); - // Unlock. - lock->prcb_of_owner.value = 0; + // Unlock with release semantics to ensure all prior writes are visible. + xe::atomic_store_release(0u, &lock->prcb_of_owner.value); if (change_irql) { // Unlock.