Read/write the cross-process diagnostic counters atomically
present_calls, frames_dropped (VideoShare) and frames_rendered (AudioStreamInfo) were plain `+= 1` / stores in the DLL, read by the host cross-process. On an x86 DLL a 64-bit store is two halves, so the x64 host could read a torn value during a carry. Benign (display-only), but a real data race. Use std::atomic_ref at the access sites rather than changing the field types: the structs stay plain POD so the layout/offset asserts are unchanged and AudioStreamInfo stays trivially copyable (it's published/read as a whole struct). The DLL writers (note_present / note_video_dropped / note_audio_frames) and the host readers (IpcServer::video_share / hook_status) now use relaxed atomic_ref; hook_status reloads frames_rendered atomically after the wholesale struct copy. The dev-tool readers (vk_validate, audio_probe) keep plain reads -- diagnostics of diagnostics, and same-bitness in practice. Validated by present_hook_test (present_calls via atomic_ref) and audio_hook_test (frames_rendered) -- also confirms no atomic_ref alignment fault. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,22 @@
|
||||
#include "ipc/ipc_server.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace coop
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
// The hook writes these cumulative diagnostic counters cross-process (an x86 DLL can do a 64-bit
|
||||
// store in two halves), so read them atomically to avoid a torn value. The shared mapping is
|
||||
// genuinely mutable -- the const here is just our read-only view -- so const_cast for atomic_ref.
|
||||
std::uint64_t atomic_load_u64(const std::uint64_t& field)
|
||||
{
|
||||
return std::atomic_ref(const_cast<std::uint64_t&>(field)).load(std::memory_order_relaxed);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool IpcServer::start(unsigned long target_pid)
|
||||
{
|
||||
std::scoped_lock lock(mutex_);
|
||||
@@ -83,6 +97,7 @@ HookStatusView IpcServer::hook_status() const
|
||||
for (std::uint32_t i = 0; i < kMaxAudioStreams; ++i)
|
||||
{
|
||||
view.audio_streams[i] = s.audio_streams[i];
|
||||
view.audio_streams[i].frames_rendered = atomic_load_u64(s.audio_streams[i].frames_rendered);
|
||||
}
|
||||
view.hook_entry_count = s.hook_entry_count;
|
||||
for (std::uint32_t i = 0; i < kMaxHookEntries; ++i)
|
||||
@@ -111,9 +126,9 @@ VideoShareView IpcServer::video_share() const
|
||||
v.width = s.width;
|
||||
v.height = s.height;
|
||||
v.format = s.format;
|
||||
v.present_calls = s.present_calls;
|
||||
v.present_calls = atomic_load_u64(s.present_calls);
|
||||
v.present_qpc = s.present_qpc;
|
||||
v.frames_dropped = s.frames_dropped;
|
||||
v.frames_dropped = atomic_load_u64(s.frames_dropped);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user