mirror of
https://git.proxmox.com/git/proxmox
synced 2025-08-07 13:33:17 +00:00
shared-memory: make tests integration tests
same as with sys/xattr, these touch files, so should use a tmpdir provided by cargo, which requires them being integration tests. if the tmpdir doesn't support O_TMPFILE (like overlayfs), the test is not run (unfortunately, there is no way to indicate this via the test result like with other test frameworks). Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
parent
540fb905c2
commit
2fa269fb05
@ -102,7 +102,7 @@ impl<T: Sized + Init> SharedMemory<T> {
|
|||||||
.ok_or_else(|| format_err!("bad path {:?}", path))?
|
.ok_or_else(|| format_err!("bad path {:?}", path))?
|
||||||
.to_owned();
|
.to_owned();
|
||||||
|
|
||||||
if !cfg!(test) {
|
if !dir_name.ends_with("shmemtest") {
|
||||||
let statfs = nix::sys::statfs::statfs(&dir_name)?;
|
let statfs = nix::sys::statfs::statfs(&dir_name)?;
|
||||||
if statfs.filesystem_type() != nix::sys::statfs::TMPFS_MAGIC {
|
if statfs.filesystem_type() != nix::sys::statfs::TMPFS_MAGIC {
|
||||||
bail!("path {:?} is not on tmpfs", dir_name);
|
bail!("path {:?} is not on tmpfs", dir_name);
|
||||||
@ -211,159 +211,4 @@ pub unsafe fn initialize_subtype<T: Init>(this: &mut T) {
|
|||||||
pub unsafe fn check_subtype<T: Init>(this: &T) -> Result<(), Error> {
|
pub unsafe fn check_subtype<T: Init>(this: &T) -> Result<(), Error> {
|
||||||
let data: &MaybeUninit<T> = std::mem::transmute(this);
|
let data: &MaybeUninit<T> = std::mem::transmute(this);
|
||||||
Init::check_type_magic(data)
|
Init::check_type_magic(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use proxmox_sys::fs::create_path;
|
|
||||||
use std::sync::atomic::AtomicU64;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::thread::spawn;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct TestData {
|
|
||||||
count: u64,
|
|
||||||
value1: u64,
|
|
||||||
value2: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Init for TestData {
|
|
||||||
fn initialize(this: &mut MaybeUninit<Self>) {
|
|
||||||
this.write(Self {
|
|
||||||
count: 0,
|
|
||||||
value1: 0xffff_ffff_ffff_0000,
|
|
||||||
value2: 0x0000_ffff_ffff_ffff,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SingleMutexData {
|
|
||||||
data: SharedMutex<TestData>,
|
|
||||||
_padding: [u8; 4096 - 64 - 8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Init for SingleMutexData {
|
|
||||||
fn initialize(this: &mut MaybeUninit<Self>) {
|
|
||||||
unsafe {
|
|
||||||
let me = &mut *this.as_mut_ptr();
|
|
||||||
initialize_subtype(&mut me.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_type_magic(this: &MaybeUninit<Self>) -> Result<(), Error> {
|
|
||||||
unsafe {
|
|
||||||
let me = &*this.as_ptr();
|
|
||||||
check_subtype(&me.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_shared_memory_mutex() -> Result<(), Error> {
|
|
||||||
create_path("../target/testdata/", None, None)?;
|
|
||||||
|
|
||||||
let shared: SharedMemory<SingleMutexData> = SharedMemory::open(
|
|
||||||
Path::new("../target/testdata/test1.shm"),
|
|
||||||
CreateOptions::new(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let shared = Arc::new(shared);
|
|
||||||
|
|
||||||
let start = shared.data().data.lock().count;
|
|
||||||
|
|
||||||
let threads: Vec<_> = (0..100)
|
|
||||||
.map(|_| {
|
|
||||||
let shared = shared.clone();
|
|
||||||
spawn(move || {
|
|
||||||
let mut guard = shared.data().data.lock();
|
|
||||||
println!("DATA {:?}", *guard);
|
|
||||||
guard.count += 1;
|
|
||||||
println!("DATA {:?}", *guard);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for thread in threads {
|
|
||||||
thread.join().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let end = shared.data().data.lock().count;
|
|
||||||
|
|
||||||
assert_eq!(end - start, 100);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
struct MultiMutexData {
|
|
||||||
acount: AtomicU64,
|
|
||||||
block1: SharedMutex<TestData>,
|
|
||||||
block2: SharedMutex<TestData>,
|
|
||||||
padding: [u8; 4096 - 136 - 16],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Init for MultiMutexData {
|
|
||||||
fn initialize(this: &mut MaybeUninit<Self>) {
|
|
||||||
unsafe {
|
|
||||||
let me = &mut *this.as_mut_ptr();
|
|
||||||
initialize_subtype(&mut me.block1);
|
|
||||||
initialize_subtype(&mut me.block2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_type_magic(this: &MaybeUninit<Self>) -> Result<(), Error> {
|
|
||||||
unsafe {
|
|
||||||
let me = &*this.as_ptr();
|
|
||||||
check_subtype(&me.block1)?;
|
|
||||||
check_subtype(&me.block2)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_shared_memory_multi_mutex() -> Result<(), Error> {
|
|
||||||
create_path("../target/testdata/", None, None)?;
|
|
||||||
|
|
||||||
let shared: SharedMemory<MultiMutexData> = SharedMemory::open(
|
|
||||||
Path::new("../target/testdata/test2.shm"),
|
|
||||||
CreateOptions::new(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let shared = Arc::new(shared);
|
|
||||||
|
|
||||||
let start1 = shared.data().block1.lock().count;
|
|
||||||
let start2 = shared.data().block2.lock().count;
|
|
||||||
|
|
||||||
let threads: Vec<_> = (0..100)
|
|
||||||
.map(|_| {
|
|
||||||
let shared = shared.clone();
|
|
||||||
spawn(move || {
|
|
||||||
let mut guard = shared.data().block1.lock();
|
|
||||||
println!("BLOCK1 {:?}", *guard);
|
|
||||||
guard.count += 1;
|
|
||||||
let mut guard = shared.data().block2.lock();
|
|
||||||
println!("BLOCK2 {:?}", *guard);
|
|
||||||
guard.count += 2;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for thread in threads {
|
|
||||||
thread.join().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let end1 = shared.data().block1.lock().count;
|
|
||||||
assert_eq!(end1 - start1, 100);
|
|
||||||
|
|
||||||
let end2 = shared.data().block2.lock().count;
|
|
||||||
assert_eq!(end2 - start2, 200);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
175
proxmox-shared-memory/tests/raw_shared_mutex.rs
Normal file
175
proxmox-shared-memory/tests/raw_shared_mutex.rs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{mem::MaybeUninit, sync::Arc, thread::spawn};
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
|
use nix::fcntl::OFlag;
|
||||||
|
use nix::sys::stat::Mode;
|
||||||
|
use nix::unistd::mkdir;
|
||||||
|
use proxmox_shared_memory::{check_subtype, initialize_subtype, Init, SharedMemory, SharedMutex};
|
||||||
|
use proxmox_sys::fs::CreateOptions;
|
||||||
|
|
||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct TestData {
|
||||||
|
count: u64,
|
||||||
|
value1: u64,
|
||||||
|
value2: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Init for TestData {
|
||||||
|
fn initialize(this: &mut MaybeUninit<Self>) {
|
||||||
|
this.write(Self {
|
||||||
|
count: 0,
|
||||||
|
value1: 0xffff_ffff_ffff_0000,
|
||||||
|
value2: 0x0000_ffff_ffff_ffff,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SingleMutexData {
|
||||||
|
data: SharedMutex<TestData>,
|
||||||
|
_padding: [u8; 4096 - 64 - 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Init for SingleMutexData {
|
||||||
|
fn initialize(this: &mut MaybeUninit<Self>) {
|
||||||
|
unsafe {
|
||||||
|
let me = &mut *this.as_mut_ptr();
|
||||||
|
initialize_subtype(&mut me.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type_magic(this: &MaybeUninit<Self>) -> Result<(), Error> {
|
||||||
|
unsafe {
|
||||||
|
let me = &*this.as_ptr();
|
||||||
|
check_subtype(&me.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct MultiMutexData {
|
||||||
|
acount: AtomicU64,
|
||||||
|
block1: SharedMutex<TestData>,
|
||||||
|
block2: SharedMutex<TestData>,
|
||||||
|
padding: [u8; 4096 - 136 - 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Init for MultiMutexData {
|
||||||
|
fn initialize(this: &mut MaybeUninit<Self>) {
|
||||||
|
unsafe {
|
||||||
|
let me = &mut *this.as_mut_ptr();
|
||||||
|
initialize_subtype(&mut me.block1);
|
||||||
|
initialize_subtype(&mut me.block2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_type_magic(this: &MaybeUninit<Self>) -> Result<(), Error> {
|
||||||
|
unsafe {
|
||||||
|
let me = &*this.as_ptr();
|
||||||
|
check_subtype(&me.block1)?;
|
||||||
|
check_subtype(&me.block2)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_test_dir<T: Init>(filename: &str) -> Option<PathBuf> {
|
||||||
|
let test_dir: String = env!("CARGO_TARGET_TMPDIR").to_string();
|
||||||
|
|
||||||
|
let mut path = PathBuf::from(&test_dir);
|
||||||
|
path.push("shmemtest");
|
||||||
|
let _ = mkdir(&path, Mode::S_IRWXU);
|
||||||
|
path.push(filename);
|
||||||
|
|
||||||
|
let oflag = OFlag::O_RDWR | OFlag::O_CLOEXEC;
|
||||||
|
|
||||||
|
// check for O_TMPFILE support
|
||||||
|
if let Err(nix::Error::Sys(nix::errno::Errno::EOPNOTSUPP)) = nix::fcntl::open(
|
||||||
|
path.parent().unwrap(),
|
||||||
|
oflag | OFlag::O_TMPFILE,
|
||||||
|
Mode::empty(),
|
||||||
|
) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(path)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_shared_memory_mutex() -> Result<(), Error> {
|
||||||
|
let path = match create_test_dir::<SingleMutexData>(&"data1.shm") {
|
||||||
|
None => { return Ok(()); }, // no O_TMPFILE support, can't run test
|
||||||
|
Some(path) => path,
|
||||||
|
};
|
||||||
|
|
||||||
|
let shared: SharedMemory<SingleMutexData> = SharedMemory::open(&path, CreateOptions::new())?;
|
||||||
|
|
||||||
|
let shared = Arc::new(shared);
|
||||||
|
|
||||||
|
let start = shared.data().data.lock().count;
|
||||||
|
|
||||||
|
let threads: Vec<_> = (0..100)
|
||||||
|
.map(|_| {
|
||||||
|
let shared = shared.clone();
|
||||||
|
spawn(move || {
|
||||||
|
let mut guard = shared.data().data.lock();
|
||||||
|
println!("DATA {:?}", *guard);
|
||||||
|
guard.count += 1;
|
||||||
|
println!("DATA {:?}", *guard);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for thread in threads {
|
||||||
|
thread.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let end = shared.data().data.lock().count;
|
||||||
|
|
||||||
|
assert_eq!(end - start, 100);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shared_memory_multi_mutex() -> Result<(), Error> {
|
||||||
|
let path = match create_test_dir::<SingleMutexData>(&"data2.shm") {
|
||||||
|
None => { return Ok(()); }, // no O_TMPFILE support, can't run test
|
||||||
|
Some(path) => path,
|
||||||
|
};
|
||||||
|
let shared: SharedMemory<MultiMutexData> = SharedMemory::open(&path, CreateOptions::new())?;
|
||||||
|
|
||||||
|
let shared = Arc::new(shared);
|
||||||
|
|
||||||
|
let start1 = shared.data().block1.lock().count;
|
||||||
|
let start2 = shared.data().block2.lock().count;
|
||||||
|
|
||||||
|
let threads: Vec<_> = (0..100)
|
||||||
|
.map(|_| {
|
||||||
|
let shared = shared.clone();
|
||||||
|
spawn(move || {
|
||||||
|
let mut guard = shared.data().block1.lock();
|
||||||
|
println!("BLOCK1 {:?}", *guard);
|
||||||
|
guard.count += 1;
|
||||||
|
let mut guard = shared.data().block2.lock();
|
||||||
|
println!("BLOCK2 {:?}", *guard);
|
||||||
|
guard.count += 2;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for thread in threads {
|
||||||
|
thread.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let end1 = shared.data().block1.lock().count;
|
||||||
|
assert_eq!(end1 - start1, 100);
|
||||||
|
|
||||||
|
let end2 = shared.data().block2.lock().count;
|
||||||
|
assert_eq!(end2 - start2, 200);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user