#![allow(clippy::semicolon_inside_block)]
use cfg_if::cfg_if;
use sem_safe::SemaphoreRef;
cfg_if! { if #[cfg(target_os = "openbsd")] {
use sem_safe::anonymous as non_named;
} else if #[cfg(feature = "plaster")] {
use sem_safe::plaster::non_named;
} else if #[cfg(all(feature = "unnamed", not(target_os = "macos")))] {
use sem_safe::unnamed as non_named;
} else if #[cfg(feature = "anonymous")] {
use sem_safe::anonymous as non_named;
} }
#[cfg(feature = "named")]
use sem_safe::named;
#[path = "../../tests/help/util.rs"]
mod util;
use util::{name_with, UnwrapOS as _};
pub(crate) fn main() {
let args = std::env::args().collect::<Vec<_>>();
let args = &args.iter().map(String::as_ref).collect::<Vec<_>>()[..];
match args {
[exec_filename] => primary(exec_filename),
[_, primary_pid, which @ ("one" | "two")] => {
let primary_pid = primary_pid.parse().expect("argument must be valid");
other(primary_pid, *which == "one");
},
_ => panic!("command-line arguments must be valid"),
}
}
fn primary(exec_filename: &str) {
use std::{os::unix::process::CommandExt as _,
process::{self, Child, Command}};
fn say(msg: &str) {
println!("primary: {msg}");
}
let sem_f = nonnamed_inter_proc_sem();
let others: [Child; 2] = ["one", "two"].map(|child_id| {
let mut other = Command::new(exec_filename);
other.arg(process::id().to_string()).arg(child_id);
unsafe { other.pre_exec(move || sem_f.post().map_errno()) };
other.spawn().unwrap()
});
sem_f.wait().unwrap_os();
sem_f.wait().unwrap_os();
say("others forked, are now exec'ing");
#[cfg(feature = "named")]
{
let [me, o1, o2] = NamedSem::triple(process::id());
o1.post();
o2.post();
me.wait();
me.wait();
say("others opened semaphores");
o2.remove_name();
o1.remove_name();
o1.post();
o2.post();
me.wait();
me.wait();
say("others coordinated");
o2.post();
o1.post();
me.wait();
me.wait();
say("others finishing");
[me, o1, o2].map(|NamedSem { sem, .. }| {
unsafe { sem.close() }.unwrap_os();
});
}
let exit_statuses = others.map(|mut child| child.wait().unwrap().success());
assert_eq!(exit_statuses, [true, true]);
}
cfg_if! { if #[cfg(feature = "named")] {
fn other(primary_pid: u32, is_alpha: bool) {
let say = |msg| println!("other{}: {msg}", if is_alpha { 1 } else { 2 });
let [p, o1, o2] = NamedSem::triple(primary_pid);
let (me, o) = if is_alpha { (o1, o2) } else { (o2, o1) };
me.wait();
say("primary opened semaphores");
p.post();
me.wait();
say("proceeding");
o.post();
me.wait();
say("other opened semaphores");
if is_alpha {
p.remove_name();
}
p.post();
me.wait();
say("finishing");
p.post();
}
use std::ffi::CString;
struct NamedSem {
sem: named::Semaphore,
name: CString,
}
impl NamedSem {
fn triple(primary_pid: u32) -> [NamedSem; 3] {
let named_sem = |name| {
let name = name_with(primary_pid, name);
let sem = named::Semaphore::open(&name, named::OpenFlags::Create {
exclusive: false, mode: 0o600,
value: 0,
}).unwrap_os();
NamedSem { sem, name }
};
["primary", "other1", "other2"].map(named_sem)
}
fn sem(&self) -> SemaphoreRef<'_> { self.sem.sem_ref() }
fn post(&self) { self.sem().post().unwrap_os(); }
fn wait(&self) { self.sem().wait().unwrap_os(); }
fn remove_name(&self) { named::Semaphore::unlink(&self.name).unwrap_os(); }
}
}
else {
fn other(_primary_pid: u32, _is_alpha: bool) {}
} }
fn nonnamed_inter_proc_sem<'l>() -> SemaphoreRef<'l> {
use non_named::Semaphore;
use std::pin::Pin;
cfg_if! { if #[cfg(all(feature = "unnamed", not(any(target_os = "macos",
target_os = "openbsd"))))] {
fn mmap_shared_sem<'l>() -> Pin<&'l mut Semaphore> {
use std::{mem::{align_of, size_of, MaybeUninit}, ptr};
use libc::{mmap, MAP_SHARED, MAP_ANONYMOUS, PROT_READ, PROT_WRITE, MAP_FAILED};
let enough_size = size_of::<Semaphore>().checked_mul(2).unwrap();
let ptr = unsafe {
mmap(ptr::null_mut(), enough_size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0)
};
assert_ne!(ptr, MAP_FAILED);
let ptr: *mut u8 = {
let ptr: *mut u8 = ptr.cast();
let align_offset = ptr.align_offset(align_of::<Semaphore>());
assert!(align_offset < size_of::<Semaphore>());
unsafe{ ptr.add(align_offset) }
};
let ptr: *mut MaybeUninit<Semaphore> = ptr.cast();
let uninit: &mut MaybeUninit<Semaphore> = unsafe { &mut *ptr };
let sem = uninit.write(Semaphore::uninit());
unsafe { Pin::new_unchecked(sem) }
}
mmap_shared_sem().into_ref().init_with(true, 0).unwrap_os()
}
else if #[cfg(feature = "anonymous")] {
use sem_safe::non_named::Semaphore as _;
static SEM_A: Semaphore = Semaphore::uninit();
Pin::static_ref(&SEM_A).init_with(0).unwrap_os()
} }
}