From 80670a5b6cafaaf6046e6c9f261294de883a76b8 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 21 Dec 2022 20:50:40 -0500
Subject: [PATCH] time: add LockFreeAtomicType

---
 src/core/hle/service/time/clock_types.h       |  1 +
 .../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>,
               "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<u64>(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<SharedMemory::Format*>(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 <typename T>
+struct LockFreeAtomicType {
+    u32 counter_;
+    std::array<T, 2> value_;
+};
+
+template <typename T>
+static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* 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 <typename T>
+static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* 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 <typename T, std::size_t Offset>
-    struct MemoryBarrier {
-        static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
-        u32_le read_attempt{};
-        std::array<T, 2> 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<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint;
-        MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context;
-        MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context;
-        MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
-        u32_le format_version;
+        LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint;
+        LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
+        LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
+        LockFreeAtomicType<bool> 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