Merge pull request #9487 from liamwhite/look-at-the-time

time: add LockFreeAtomicType
This commit is contained in:
liamwhite 2022-12-25 12:50:57 -05:00 committed by GitHub
commit ea70d9c79e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 40 deletions

View file

@ -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;

View file

@ -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

View file

@ -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