diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/time/clock_types.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/time/time_sharedmemory.cpp | 17 | ||||
| -rw-r--r-- | 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 { | |||
| 49 | static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); | 49 | static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size"); |
| 50 | static_assert(std::is_trivially_copyable_v<SteadyClockContext>, | 50 | static_assert(std::is_trivially_copyable_v<SteadyClockContext>, |
| 51 | "SteadyClockContext must be trivially copyable"); | 51 | "SteadyClockContext must be trivially copyable"); |
| 52 | using StandardSteadyClockTimePointType = SteadyClockContext; | ||
| 52 | 53 | ||
| 53 | struct SystemClockContext { | 54 | struct SystemClockContext { |
| 54 | s64 offset; | 55 | 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, | |||
| 26 | const Clock::SteadyClockContext context{ | 26 | const Clock::SteadyClockContext context{ |
| 27 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), | 27 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), |
| 28 | clock_source_id}; | 28 | clock_source_id}; |
| 29 | shared_memory_format.standard_steady_clock_timepoint.StoreData( | 29 | StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context); |
| 30 | system.Kernel().GetTimeSharedMem().GetPointer(), context); | ||
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { | 32 | void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) { |
| 34 | shared_memory_format.standard_local_system_clock_context.StoreData( | 33 | StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context); |
| 35 | system.Kernel().GetTimeSharedMem().GetPointer(), context); | ||
| 36 | } | 34 | } |
| 37 | 35 | ||
| 38 | void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { | 36 | void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) { |
| 39 | shared_memory_format.standard_network_system_clock_context.StoreData( | 37 | StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context); |
| 40 | system.Kernel().GetTimeSharedMem().GetPointer(), context); | ||
| 41 | } | 38 | } |
| 42 | 39 | ||
| 43 | void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { | 40 | void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) { |
| 44 | shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( | 41 | StoreToLockFreeAtomicType( |
| 45 | system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled); | 42 | &GetFormat()->is_standard_user_system_clock_automatic_correction_enabled, is_enabled); |
| 43 | } | ||
| 44 | |||
| 45 | SharedMemory::Format* SharedMemory::GetFormat() { | ||
| 46 | return reinterpret_cast<SharedMemory::Format*>(system.Kernel().GetTimeSharedMem().GetPointer()); | ||
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | } // namespace Service::Time | 49 | } // 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 @@ | |||
| 10 | 10 | ||
| 11 | namespace Service::Time { | 11 | namespace Service::Time { |
| 12 | 12 | ||
| 13 | // Note: this type is not safe for concurrent writes. | ||
| 14 | template <typename T> | ||
| 15 | struct LockFreeAtomicType { | ||
| 16 | u32 counter_; | ||
| 17 | std::array<T, 2> value_; | ||
| 18 | }; | ||
| 19 | |||
| 20 | template <typename T> | ||
| 21 | static inline void StoreToLockFreeAtomicType(LockFreeAtomicType<T>* p, const T& value) { | ||
| 22 | // Get the current counter. | ||
| 23 | auto counter = p->counter_; | ||
| 24 | |||
| 25 | // Increment the counter. | ||
| 26 | ++counter; | ||
| 27 | |||
| 28 | // Store the updated value. | ||
| 29 | p->value_[counter % 2] = value; | ||
| 30 | |||
| 31 | // Fence memory. | ||
| 32 | std::atomic_thread_fence(std::memory_order_release); | ||
| 33 | |||
| 34 | // Set the updated counter. | ||
| 35 | p->counter_ = counter; | ||
| 36 | } | ||
| 37 | |||
| 38 | template <typename T> | ||
| 39 | static inline T LoadFromLockFreeAtomicType(const LockFreeAtomicType<T>* p) { | ||
| 40 | while (true) { | ||
| 41 | // Get the counter. | ||
| 42 | auto counter = p->counter_; | ||
| 43 | |||
| 44 | // Get the value. | ||
| 45 | auto value = p->value_[counter % 2]; | ||
| 46 | |||
| 47 | // Fence memory. | ||
| 48 | std::atomic_thread_fence(std::memory_order_acquire); | ||
| 49 | |||
| 50 | // Check that the counter matches. | ||
| 51 | if (counter == p->counter_) { | ||
| 52 | return value; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 13 | class SharedMemory final { | 57 | class SharedMemory final { |
| 14 | public: | 58 | public: |
| 15 | explicit SharedMemory(Core::System& system_); | 59 | explicit SharedMemory(Core::System& system_); |
| 16 | ~SharedMemory(); | 60 | ~SharedMemory(); |
| 17 | 61 | ||
| 18 | // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this? | ||
| 19 | template <typename T, std::size_t Offset> | ||
| 20 | struct MemoryBarrier { | ||
| 21 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||
| 22 | u32_le read_attempt{}; | ||
| 23 | std::array<T, 2> data{}; | ||
| 24 | |||
| 25 | // These are not actually memory barriers at the moment as we don't have multicore and all | ||
| 26 | // HLE is mutexed. This will need to properly be implemented when we start updating the time | ||
| 27 | // points on threads. As of right now, we'll be updated both values synchronously and just | ||
| 28 | // incrementing the read_attempt to indicate that we waited. | ||
| 29 | void StoreData(u8* shared_memory, T data_to_store) { | ||
| 30 | std::memcpy(this, shared_memory + Offset, sizeof(*this)); | ||
| 31 | read_attempt++; | ||
| 32 | data[read_attempt & 1] = data_to_store; | ||
| 33 | std::memcpy(shared_memory + Offset, this, sizeof(*this)); | ||
| 34 | } | ||
| 35 | |||
| 36 | // For reading we're just going to read the last stored value. If there was no value stored | ||
| 37 | // it will just end up reading an empty value as intended. | ||
| 38 | T ReadData(u8* shared_memory) { | ||
| 39 | std::memcpy(this, shared_memory + Offset, sizeof(*this)); | ||
| 40 | return data[(read_attempt - 1) & 1]; | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | |||
| 44 | // Shared memory format | 62 | // Shared memory format |
| 45 | struct Format { | 63 | struct Format { |
| 46 | MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint; | 64 | LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint; |
| 47 | MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context; | 65 | LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context; |
| 48 | MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context; | 66 | LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context; |
| 49 | MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction; | 67 | LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled; |
| 50 | u32_le format_version; | 68 | u32 format_version; |
| 51 | }; | 69 | }; |
| 70 | static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0); | ||
| 71 | static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38); | ||
| 72 | static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80); | ||
| 73 | static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) == | ||
| 74 | 0xc8); | ||
| 52 | static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); | 75 | static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); |
| 53 | 76 | ||
| 54 | void SetupStandardSteadyClock(const Common::UUID& clock_source_id, | 77 | void SetupStandardSteadyClock(const Common::UUID& clock_source_id, |
| @@ -56,10 +79,10 @@ public: | |||
| 56 | void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); | 79 | void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context); |
| 57 | void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); | 80 | void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context); |
| 58 | void SetAutomaticCorrectionEnabled(bool is_enabled); | 81 | void SetAutomaticCorrectionEnabled(bool is_enabled); |
| 82 | Format* GetFormat(); | ||
| 59 | 83 | ||
| 60 | private: | 84 | private: |
| 61 | Core::System& system; | 85 | Core::System& system; |
| 62 | Format shared_memory_format{}; | ||
| 63 | }; | 86 | }; |
| 64 | 87 | ||
| 65 | } // namespace Service::Time | 88 | } // namespace Service::Time |