From 4bd5f7dd5984feccee024ce22faea81f4775f571 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Mon, 2 Feb 2026 16:10:32 +0100 Subject: [PATCH] util/rust: Add atomic memory synchronization support Add AtomicMemorySentinel for cross-platform atomic memory synchronization. This provides a platform-agnostic API over Linux futex syscalls, allowing shared memory synchronization via memory-mapped file descriptors. Includes Linux futex implementation and stub implementations for Windows and other platforms. Signed-off-by: Dorinda Bassey --- src/util/rust/atomic_memory_sentinel.rs | 88 +++++++++++++++++++ src/util/rust/defines.rs | 1 + src/util/rust/lib.rs | 2 + .../rust/sys/linux/atomic_memory_sentinel.rs | 40 +++++++++ src/util/rust/sys/linux/descriptor.rs | 8 ++ src/util/rust/sys/linux/mod.rs | 1 + .../rust/sys/stub/atomic_memory_sentinel.rs | 24 +++++ src/util/rust/sys/stub/mod.rs | 1 + .../sys/windows/atomic_memory_sentinel.rs | 24 +++++ src/util/rust/sys/windows/mod.rs | 1 + .../packagefiles/rustix-1-rs/meson.build | 1 + 11 files changed, 191 insertions(+) create mode 100644 src/util/rust/atomic_memory_sentinel.rs create mode 100644 src/util/rust/sys/linux/atomic_memory_sentinel.rs create mode 100644 src/util/rust/sys/stub/atomic_memory_sentinel.rs create mode 100644 src/util/rust/sys/windows/atomic_memory_sentinel.rs diff --git a/src/util/rust/atomic_memory_sentinel.rs b/src/util/rust/atomic_memory_sentinel.rs new file mode 100644 index 00000000000..ad7de31cb0a --- /dev/null +++ b/src/util/rust/atomic_memory_sentinel.rs @@ -0,0 +1,88 @@ +// Copyright 2026 Red Hat, Inc. +// SPDX-License-Identifier: MIT + +//! AtomicMemorySentinel - Cross-platform atomic memory synchronization primitive. +//! +//! Provides access to futex and WaitOnAddress-like APIs for atomic memory operations. + +use std::mem::size_of; +use std::sync::atomic::{AtomicU32, Ordering}; + +use crate::sys::platform::atomic_memory_sentinel; +use crate::MappedRegion; +use crate::MemoryMapping; +use crate::MesaError; +use crate::MesaResult; + +/// A sentinel that provides access to atomic memory. +/// Safe to share across threads. +pub struct AtomicMemorySentinel { + _memory_mapping: MemoryMapping, + atomic_ptr: *mut u32, +} + +// SAFETY: AtomicMemorySentinel can be sent across threads. +unsafe impl Send for AtomicMemorySentinel {} +// SAFETY: AtomicMemorySentinel can be shared across threads. +unsafe impl Sync for AtomicMemorySentinel {} + +impl AtomicMemorySentinel { + /// Create a new AtomicMemorySentinel from a memory mapping. + pub fn new(memory_mapping: MemoryMapping) -> MesaResult { + if memory_mapping.size() < size_of::() { + return Err(MesaError::WithContext( + "memory mapping too small for AtomicU32", + )); + } + + let atomic_ptr = memory_mapping.as_ptr() as *mut u32; + + Ok(Self { + _memory_mapping: memory_mapping, + atomic_ptr, + }) + } + + /// Signal that the atomic memory has changed. + /// + /// This will wake up waiters on this sentinel. + pub fn signal(&self) -> MesaResult<()> { + // SAFETY: self.atomic_ptr is valid for the lifetime of the MemoryMapping, + // properly aligned, and we have exclusive ownership via the MemoryMapping. + let atomic_val = unsafe { AtomicU32::from_ptr(self.atomic_ptr) }; + atomic_memory_sentinel::wake_bitset(atomic_val, i32::MAX, 1); + Ok(()) + } + + /// Load the current value from atomic memory. + pub fn load(&self) -> u32 { + // SAFETY: self.atomic_ptr is valid for the lifetime of the MemoryMapping, + // properly aligned, and we have exclusive ownership via the MemoryMapping. + let atomic_val = unsafe { AtomicU32::from_ptr(self.atomic_ptr) }; + atomic_val.load(Ordering::SeqCst) + } + + /// Store a value to atomic memory. + pub fn store(&self, val: u32) { + // SAFETY: self.atomic_ptr is valid for the lifetime of the MemoryMapping, + // properly aligned, and we have exclusive ownership via the MemoryMapping. + let atomic_val = unsafe { AtomicU32::from_ptr(self.atomic_ptr) }; + atomic_val.store(val, Ordering::SeqCst); + } + + /// Wake all threads waiting on this sentinel. + pub fn wake_all(&self) { + // SAFETY: self.atomic_ptr is valid for the lifetime of the MemoryMapping, + // properly aligned, and we have exclusive ownership via the MemoryMapping. + let atomic_val = unsafe { AtomicU32::from_ptr(self.atomic_ptr) }; + atomic_memory_sentinel::wake_all(atomic_val); + } + + /// Blocks until the value changes from `val` or is woken up. + pub fn wait(&self, val: u32) { + // SAFETY: self.atomic_ptr is valid for the lifetime of the MemoryMapping, + // properly aligned, and we have exclusive ownership via the MemoryMapping. + let atomic_val = unsafe { AtomicU32::from_ptr(self.atomic_ptr) }; + atomic_memory_sentinel::wait_bitset(atomic_val, val, 1); + } +} diff --git a/src/util/rust/defines.rs b/src/util/rust/defines.rs index d55b9c3e0ac..b9df0233a7c 100644 --- a/src/util/rust/defines.rs +++ b/src/util/rust/defines.rs @@ -87,6 +87,7 @@ pub enum DescriptorType { Unknown, Memory(u32, u32), // (size, handle_type) WritePipe, + Event, } /// # Safety diff --git a/src/util/rust/lib.rs b/src/util/rust/lib.rs index db032265502..bb9804c7752 100644 --- a/src/util/rust/lib.rs +++ b/src/util/rust/lib.rs @@ -1,6 +1,7 @@ // Copyright 2025 Google // SPDX-License-Identifier: MIT +mod atomic_memory_sentinel; mod bytestream; mod defines; mod descriptor; @@ -9,6 +10,7 @@ mod memory_mapping; mod shm; mod sys; +pub use atomic_memory_sentinel::AtomicMemorySentinel; pub use bytestream::Reader; pub use bytestream::Writer; pub use defines::*; diff --git a/src/util/rust/sys/linux/atomic_memory_sentinel.rs b/src/util/rust/sys/linux/atomic_memory_sentinel.rs new file mode 100644 index 00000000000..f388f43110b --- /dev/null +++ b/src/util/rust/sys/linux/atomic_memory_sentinel.rs @@ -0,0 +1,40 @@ +// Copyright 2026 Red Hat, Inc. +// SPDX-License-Identifier: MIT + +//! Linux futex wrappers for cross-domain synchronization. + +use std::num::NonZeroU32; +use std::sync::atomic::AtomicU32; + +use rustix::thread::futex; + +/// Wait on a futex with a bitset mask. +/// +/// Blocks until the futex is woken up or the value changes from `val`. +pub fn wait_bitset(atomic_val: &AtomicU32, val: u32, bitset: u32) { + let flags = futex::Flags::PRIVATE; + let timeout = None; + let val3 = NonZeroU32::new(bitset).unwrap_or(NonZeroU32::new(1).unwrap()); + + // Ignore errors - futex::wait_bitset returns an error if the value + // has already changed or if interrupted, both are expected + let _ = futex::wait_bitset(atomic_val, flags, val, timeout, val3); +} + +/// Wake threads waiting on a futex with a bitset mask. +/// +/// Only wakes threads whose bitset matches the provided mask. +pub fn wake_bitset(atomic_val: &AtomicU32, val: i32, bitset: u32) { + let flags = futex::Flags::PRIVATE; + let val_u32 = if val < 0 { i32::MAX as u32 } else { val as u32 }; + let val3 = NonZeroU32::new(bitset).unwrap_or(NonZeroU32::new(1).unwrap()); + + let _ = futex::wake_bitset(atomic_val, flags, val_u32, val3); +} + +/// Wake all threads waiting on a futex. +pub fn wake_all(atomic_val: &AtomicU32) { + let flags = futex::Flags::PRIVATE; + // u32::MAX means wake all waiters + let _ = futex::wake(atomic_val, flags, u32::MAX); +} diff --git a/src/util/rust/sys/linux/descriptor.rs b/src/util/rust/sys/linux/descriptor.rs index 8e50006ed56..a5897bdfede 100644 --- a/src/util/rust/sys/linux/descriptor.rs +++ b/src/util/rust/sys/linux/descriptor.rs @@ -41,6 +41,14 @@ impl OwnedDescriptor { } pub fn determine_type(&self) -> Result { + // Check for eventfd first (not seekable, special symlink) + if let Ok(fd_path) = read_link(format!("/proc/self/fd/{}", self.as_raw_descriptor())) { + let path_str = fd_path.to_string_lossy(); + if path_str.starts_with("anon_inode:[eventfd]") { + return Ok(DescriptorType::Event); + } + } + match seek(&self.owned, SeekFrom::End(0)) { Ok(seek_size) => { let size: u32 = seek_size diff --git a/src/util/rust/sys/linux/mod.rs b/src/util/rust/sys/linux/mod.rs index 185538ec73c..77e7692fa44 100644 --- a/src/util/rust/sys/linux/mod.rs +++ b/src/util/rust/sys/linux/mod.rs @@ -1,6 +1,7 @@ // Copyright 2025 Google // SPDX-License-Identifier: MIT +pub mod atomic_memory_sentinel; pub mod descriptor; pub mod event; pub mod memory_mapping; diff --git a/src/util/rust/sys/stub/atomic_memory_sentinel.rs b/src/util/rust/sys/stub/atomic_memory_sentinel.rs new file mode 100644 index 00000000000..9e034fc58fc --- /dev/null +++ b/src/util/rust/sys/stub/atomic_memory_sentinel.rs @@ -0,0 +1,24 @@ +// Copyright 2026 Red Hat, Inc. +// SPDX-License-Identifier: MIT + +//! Stub atomic memory synchronization implementation. +//! +//! The Linux backend uses futexes for atomic memory synchronization. +//! This stub implementation allows compilation on platforms without native support. + +use std::sync::atomic::AtomicU32; + +/// Stub implementation - no-op on non-Linux platforms. +pub fn wait_bitset(_atomic_val: &AtomicU32, _val: u32, _bitset: u32) { + todo!("atomic memory synchronization not implemented on this platform") +} + +/// Stub implementation - no-op on non-Linux platforms. +pub fn wake_bitset(_atomic_val: &AtomicU32, _val: i32, _bitset: u32) { + todo!("atomic memory synchronization not implemented on this platform") +} + +/// Stub implementation - no-op on non-Linux platforms. +pub fn wake_all(_atomic_val: &AtomicU32) { + todo!("atomic memory synchronization not implemented on this platform") +} diff --git a/src/util/rust/sys/stub/mod.rs b/src/util/rust/sys/stub/mod.rs index 185538ec73c..77e7692fa44 100644 --- a/src/util/rust/sys/stub/mod.rs +++ b/src/util/rust/sys/stub/mod.rs @@ -1,6 +1,7 @@ // Copyright 2025 Google // SPDX-License-Identifier: MIT +pub mod atomic_memory_sentinel; pub mod descriptor; pub mod event; pub mod memory_mapping; diff --git a/src/util/rust/sys/windows/atomic_memory_sentinel.rs b/src/util/rust/sys/windows/atomic_memory_sentinel.rs new file mode 100644 index 00000000000..535f28928bb --- /dev/null +++ b/src/util/rust/sys/windows/atomic_memory_sentinel.rs @@ -0,0 +1,24 @@ +// Copyright 2026 Red Hat, Inc. +// SPDX-License-Identifier: MIT + +//! Stub atomic memory synchronization implementation for Windows. +//! +//! The Linux backend uses futexes for atomic memory synchronization. +//! This stub implementation allows compilation on Windows without native support. + +use std::sync::atomic::AtomicU32; + +/// Stub implementation - no-op on Windows. +pub fn wait_bitset(_atomic_val: &AtomicU32, _val: u32, _bitset: u32) { + todo!("atomic memory synchronization not implemented on this platform") +} + +/// Stub implementation - no-op on Windows. +pub fn wake_bitset(_atomic_val: &AtomicU32, _val: i32, _bitset: u32) { + todo!("atomic memory synchronization not implemented on this platform") +} + +/// Stub implementation - no-op on Windows. +pub fn wake_all(_atomic_val: &AtomicU32) { + todo!("atomic memory synchronization not implemented on this platform") +} diff --git a/src/util/rust/sys/windows/mod.rs b/src/util/rust/sys/windows/mod.rs index 185538ec73c..77e7692fa44 100644 --- a/src/util/rust/sys/windows/mod.rs +++ b/src/util/rust/sys/windows/mod.rs @@ -1,6 +1,7 @@ // Copyright 2025 Google // SPDX-License-Identifier: MIT +pub mod atomic_memory_sentinel; pub mod descriptor; pub mod event; pub mod memory_mapping; diff --git a/subprojects/packagefiles/rustix-1-rs/meson.build b/subprojects/packagefiles/rustix-1-rs/meson.build index 7bb4216d737..9c9e6c174ac 100644 --- a/subprojects/packagefiles/rustix-1-rs/meson.build +++ b/subprojects/packagefiles/rustix-1-rs/meson.build @@ -30,6 +30,7 @@ if host_machine.system() == 'linux' or host_machine.system() == 'android' '--cfg', 'feature="net"', '--cfg', 'feature="param"', '--cfg', 'feature="pipe"', + '--cfg', 'feature="thread"', ] elif host_machine.system() == 'darwin' or host_machine.system() == 'macos' rustix_args += [