use std::collections::HashMap;
use std::ffi::CString;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex};
const NSLOTS: usize = 32;
const SLOT_DATA: usize = 8192;
const SLOT_HDR: usize = 16; const SLOT_SIZE: usize = SLOT_HDR + SLOT_DATA;
const RING_SIZE: usize = NSLOTS * SLOT_SIZE;
const EMPTY: u32 = 0;
const FULL: u32 = 1;
pub type OnRecv = Arc<dyn Fn(Vec<u8>) + Send + Sync>;
struct ShmRing {
ptr: *mut u8,
name: CString,
creator: bool,
idx: usize, }
unsafe impl Send for ShmRing {}
impl ShmRing {
fn slot(&self, i: usize) -> *mut u8 {
unsafe { self.ptr.add(i * SLOT_SIZE) }
}
fn state(&self, i: usize) -> &AtomicU32 {
unsafe { &*(self.slot(i) as *const AtomicU32) }
}
fn set_more(&self, i: usize, more: u32) {
unsafe { ((self.slot(i) as *mut u32).add(1)).write_volatile(more) }
}
fn more(&self, i: usize) -> u32 {
unsafe { ((self.slot(i) as *const u32).add(1)).read_volatile() }
}
fn set_len(&self, i: usize, len: u32) {
unsafe { ((self.slot(i) as *mut u32).add(2)).write_volatile(len) }
}
fn len(&self, i: usize) -> u32 {
unsafe { ((self.slot(i) as *const u32).add(2)).read_volatile() }
}
fn data(&self, i: usize) -> *mut u8 {
unsafe { self.slot(i).add(SLOT_HDR) }
}
fn open(name: String, creator: bool) -> Option<ShmRing> {
let cname = CString::new(name).ok()?;
unsafe {
let fd = if creator {
let fd = libc::shm_open(
cname.as_ptr(),
libc::O_CREAT | libc::O_RDWR,
0o600 as libc::c_uint,
);
if fd < 0 {
return None;
}
if libc::ftruncate(fd, RING_SIZE as libc::off_t) != 0 {
}
fd
} else {
let mut tries = 0;
loop {
let fd = libc::shm_open(cname.as_ptr(), libc::O_RDWR, 0o600 as libc::c_uint);
if fd >= 0 {
break fd;
}
tries += 1;
if tries > 5000 {
return None;
}
std::thread::sleep(std::time::Duration::from_millis(1));
}
};
let ptr = libc::mmap(
std::ptr::null_mut(),
RING_SIZE,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0,
);
libc::close(fd);
if ptr == libc::MAP_FAILED {
return None;
}
Some(ShmRing {
ptr: ptr as *mut u8,
name: cname,
creator,
idx: 0,
})
}
}
fn write(&mut self, framed: &[u8]) {
let mut off = 0;
loop {
let remaining = framed.len() - off;
let n = remaining.min(SLOT_DATA);
let more = if off + n < framed.len() { 1 } else { 0 };
let i = self.idx;
while self.state(i).load(Ordering::Acquire) != EMPTY {
std::thread::yield_now();
}
self.set_len(i, n as u32);
self.set_more(i, more);
unsafe {
std::ptr::copy_nonoverlapping(framed.as_ptr().add(off), self.data(i), n);
}
self.state(i).store(FULL, Ordering::Release);
self.idx = (self.idx + 1) % NSLOTS;
off += n;
if more == 0 {
break;
}
}
}
fn drain(&mut self, acc: &mut Vec<u8>, on_recv: &OnRecv) -> bool {
let mut progressed = false;
loop {
let i = self.idx;
if self.state(i).load(Ordering::Acquire) != FULL {
break;
}
let n = self.len(i) as usize;
let more = self.more(i);
let start = acc.len();
acc.resize(start + n, 0);
unsafe {
std::ptr::copy_nonoverlapping(self.data(i), acc.as_mut_ptr().add(start), n);
}
self.state(i).store(EMPTY, Ordering::Release);
self.idx = (self.idx + 1) % NSLOTS;
progressed = true;
if more == 0 {
let msg = std::mem::take(acc);
on_recv(msg);
}
}
progressed
}
}
impl Drop for ShmRing {
fn drop(&mut self) {
unsafe {
libc::munmap(self.ptr as *mut libc::c_void, RING_SIZE);
if self.creator {
libc::shm_unlink(self.name.as_ptr());
}
}
}
}
pub(crate) struct ShmTransport {
writers: HashMap<i32, Mutex<ShmRing>>,
}
impl ShmTransport {
pub(crate) fn init(
jobid: u64,
rank: i32,
same_host: &[i32],
on_recv: OnRecv,
) -> Option<ShmTransport> {
if same_host.is_empty() {
return None;
}
let mut writers = HashMap::new();
for &peer in same_host {
let name = format!("/mpi{jobid}.{rank}.{peer}");
if let Some(ring) = ShmRing::open(name, true) {
writers.insert(peer, Mutex::new(ring));
}
}
let reader_names: Vec<String> = same_host
.iter()
.map(|&peer| format!("/mpi{jobid}.{peer}.{rank}"))
.collect();
std::thread::spawn(move || {
let mut readers: Vec<(ShmRing, Vec<u8>)> = reader_names
.into_iter()
.filter_map(|n| ShmRing::open(n, false).map(|r| (r, Vec::new())))
.collect();
loop {
let mut any = false;
for (ring, acc) in readers.iter_mut() {
any |= ring.drain(acc, &on_recv);
}
if !any {
std::thread::yield_now();
}
}
});
Some(ShmTransport { writers })
}
pub(crate) fn try_send(&self, dest: i32, framed: &[u8]) -> bool {
match self.writers.get(&dest) {
Some(ring) => {
ring.lock().unwrap().write(framed);
true
}
None => false,
}
}
}