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/clock_types.h | 1 + src/core/hle/service/time/time_sharedmemory.cpp | 17 ++--- src/core/hle/service/time/time_sharedmemory.h | 87 ++++++++++++++++--------- 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h index ef070f32f..ed1eb5b2d 100644 --- a/src/core/hle/service/time/clock_types.h +++ b/src/core/hle/service/time/clock_types.h @@ -49,6 +49,7 @@ struct SteadyClockContext { static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); static_assert(std::is_trivially_copyable_v, "SteadyClockContext must be trivially copyable"); +using StandardSteadyClockTimePointType = SteadyClockContext; struct SystemClockContext { s64 offset; diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index a3aa0e77f..ff53a7d6f 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -26,23 +26,24 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id, const Clock::SteadyClockContext context{ static_cast(current_time_point.nanoseconds - ticks_time_span.nanoseconds), clock_source_id}; - shared_memory_format.standard_steady_clock_timepoint.StoreData( - system.Kernel().GetTimeSharedMem().GetPointer(), context); + StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context); } void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { - shared_memory_format.standard_local_system_clock_context.StoreData( - system.Kernel().GetTimeSharedMem().GetPointer(), context); + StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context); } void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { - shared_memory_format.standard_network_system_clock_context.StoreData( - system.Kernel().GetTimeSharedMem().GetPointer(), context); + StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context); } void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { - shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( - system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled); + StoreToLockFreeAtomicType( + &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled); +} + +SharedMemory::Format* SharedMemory::GetFormat() { + return reinterpret_cast(system.Kernel().GetTimeSharedMem().GetPointer()); } } // namespace Service::Time 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