forked from suyu/suyu
time: add LockFreeAtomicType
This commit is contained in:
parent
1b11e0f0d3
commit
80670a5b6c
3 changed files with 65 additions and 40 deletions
|
@ -49,6 +49,7 @@ struct SteadyClockContext {
|
||||||
static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
|
static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
|
||||||
static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
|
static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
|
||||||
"SteadyClockContext must be trivially copyable");
|
"SteadyClockContext must be trivially copyable");
|
||||||
|
using StandardSteadyClockTimePointType = SteadyClockContext;
|
||||||
|
|
||||||
struct SystemClockContext {
|
struct SystemClockContext {
|
||||||
s64 offset;
|
s64 offset;
|
||||||
|
|
|
@ -26,23 +26,24 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
|
||||||
const Clock::SteadyClockContext context{
|
const Clock::SteadyClockContext context{
|
||||||
static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
|
static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
|
||||||
clock_source_id};
|
clock_source_id};
|
||||||
shared_memory_format.standard_steady_clock_timepoint.StoreData(
|
StoreToLockFreeAtomicType(&GetFormat()->standard_steady_clock_timepoint, context);
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
|
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
|
||||||
shared_memory_format.standard_local_system_clock_context.StoreData(
|
StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
|
void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
|
||||||
shared_memory_format.standard_network_system_clock_context.StoreData(
|
StoreToLockFreeAtomicType(&GetFormat()->standard_network_system_clock_context, context);
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
|
void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
|
||||||
shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
|
StoreToLockFreeAtomicType(
|
||||||
system.Kernel().GetTimeSharedMem().GetPointer(), is_enabled);
|
&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
|
} // namespace Service::Time
|
||||||
|
|
|
@ -10,45 +10,68 @@
|
||||||
|
|
||||||
namespace Service::Time {
|
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 {
|
class SharedMemory final {
|
||||||
public:
|
public:
|
||||||
explicit SharedMemory(Core::System& system_);
|
explicit SharedMemory(Core::System& system_);
|
||||||
~SharedMemory();
|
~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
|
// Shared memory format
|
||||||
struct Format {
|
struct Format {
|
||||||
MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint;
|
LockFreeAtomicType<Clock::StandardSteadyClockTimePointType> standard_steady_clock_timepoint;
|
||||||
MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context;
|
LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
|
||||||
MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context;
|
LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
|
||||||
MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
|
LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled;
|
||||||
u32_le format_version;
|
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");
|
static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
|
||||||
|
|
||||||
void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
|
void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
|
||||||
|
@ -56,10 +79,10 @@ public:
|
||||||
void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
|
void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
|
||||||
void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
|
void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
|
||||||
void SetAutomaticCorrectionEnabled(bool is_enabled);
|
void SetAutomaticCorrectionEnabled(bool is_enabled);
|
||||||
|
Format* GetFormat();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
Format shared_memory_format{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::Time
|
} // namespace Service::Time
|
||||||
|
|
Loading…
Reference in a new issue