From 80670a5b6cafaaf6046e6c9f261294de883a76b8 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 21 Dec 2022 20:50:40 -0500 Subject: time: add LockFreeAtomicType --- src/core/hle/service/time/time_sharedmemory.h | 87 +++++++++++++++++---------- 1 file changed, 55 insertions(+), 32 deletions(-) (limited to 'src/core/hle/service/time/time_sharedmemory.h') diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h index 561685acd..044a4d24e 100644 --- a/src/core/hle/service/time/time_sharedmemory.h +++ b/src/core/hle/service/time/time_sharedmemory.h @@ -10,45 +10,68 @@ namespace Service::Time { +// Note: this type is not safe for concurrent writes. +template +struct LockFreeAtomicType { + u32 counter_; + std::array value_; +}; + +template +static inline void StoreToLockFreeAtomicType(LockFreeAtomicType* p, const T& value) { + // Get the current counter. + auto counter = p->counter_; + + // Increment the counter. + ++counter; + + // Store the updated value. + p->value_[counter % 2] = value; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_release); + + // Set the updated counter. + p->counter_ = counter; +} + +template +static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType* p) { + while (true) { + // Get the counter. + auto counter = p->counter_; + + // Get the value. + auto value = p->value_[counter % 2]; + + // Fence memory. + std::atomic_thread_fence(std::memory_order_acquire); + + // Check that the counter matches. + if (counter == p->counter_) { + return value; + } + } +} + class SharedMemory final { public: explicit SharedMemory(Core::System& system_); ~SharedMemory(); - // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this? - template - struct MemoryBarrier { - static_assert(std::is_trivially_copyable_v, "T must be trivially copyable"); - u32_le read_attempt{}; - std::array data{}; - - // These are not actually memory barriers at the moment as we don't have multicore and all - // HLE is mutexed. This will need to properly be implemented when we start updating the time - // points on threads. As of right now, we'll be updated both values synchronously and just - // incrementing the read_attempt to indicate that we waited. - void StoreData(u8* shared_memory, T data_to_store) { - std::memcpy(this, shared_memory + Offset, sizeof(*this)); - read_attempt++; - data[read_attempt & 1] = data_to_store; - std::memcpy(shared_memory + Offset, this, sizeof(*this)); - } - - // For reading we're just going to read the last stored value. If there was no value stored - // it will just end up reading an empty value as intended. - T ReadData(u8* shared_memory) { - std::memcpy(this, shared_memory + Offset, sizeof(*this)); - return data[(read_attempt - 1) & 1]; - } - }; - // Shared memory format struct Format { - MemoryBarrier standard_steady_clock_timepoint; - MemoryBarrier standard_local_system_clock_context; - MemoryBarrier standard_network_system_clock_context; - MemoryBarrier standard_user_system_clock_automatic_correction; - u32_le format_version; + LockFreeAtomicType standard_steady_clock_timepoint; + LockFreeAtomicType standard_local_system_clock_context; + LockFreeAtomicType standard_network_system_clock_context; + LockFreeAtomicType is_standard_user_system_clock_automatic_correction_enabled; + u32 format_version; }; + static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0); + static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38); + static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80); + static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) == + 0xc8); static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); void SetupStandardSteadyClock(const Common::UUID& clock_source_id, @@ -56,10 +79,10 @@ public: void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); void SetAutomaticCorrectionEnabled(bool is_enabled); + Format* GetFormat(); private: Core::System& system; - Format shared_memory_format{}; }; } // namespace Service::Time -- cgit v1.2.3