1use std::collections::{HashMap, VecDeque};
17use std::io::{self, Read, Write};
18use std::net::{SocketAddr, TcpListener, TcpStream};
19use std::sync::atomic::{AtomicBool, Ordering};
20use std::sync::{Arc, Condvar, Mutex, OnceLock};
21use std::thread;
22use std::time::Duration;
23
24pub const ANY_SOURCE: i32 = -1;
26pub const ANY_TAG: i32 = -1;
28
29const MAGIC: u32 = 0x4D50_4921; const HEADER_LEN: usize = 4 + 4 + 4 + 4 + 4 + 8 + 4 + 8; #[cfg(target_endian = "big")]
36const MY_ENDIAN: &str = "be";
37#[cfg(target_endian = "little")]
38const MY_ENDIAN: &str = "le";
39
40pub type ParentBlock = (u32, Vec<SocketAddr>);
43
44const ABORT_CONTEXT: u32 = 0x7AB0_0117;
48
49const RNDV_CTS_CTEXT: u32 = 0x7AB0_0C75;
51const RNDV_DATA_CTEXT: u32 = 0x7AB0_0DA7;
53const RTS_BIT: u32 = 0x8000_0000;
55const RNDV_THRESHOLD: usize = 64 * 1024;
58
59static ABORTING: AtomicBool = AtomicBool::new(false);
61
62static RNDV_OUT: Mutex<Option<HashMap<u64, Vec<u8>>>> = Mutex::new(None);
65static RNDV_SEQ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
67
68#[derive(Debug)]
70struct Incoming {
71 comm: u32,
72 source: i32,
73 tag: i32,
74 count: u64,
75 datatype: u32,
76 payload: Vec<u8>,
77}
78
79#[derive(Debug, Clone, Copy)]
81struct Header {
82 comm: u32,
83 source: i32,
84 dest: i32,
85 tag: i32,
86 count: u64,
87 datatype: u32,
88 len: u64,
89}
90
91impl Header {
92 fn to_bytes(self) -> [u8; HEADER_LEN] {
93 let mut b = [0u8; HEADER_LEN];
94 b[0..4].copy_from_slice(&MAGIC.to_le_bytes());
95 b[4..8].copy_from_slice(&self.comm.to_le_bytes());
96 b[8..12].copy_from_slice(&self.source.to_le_bytes());
97 b[12..16].copy_from_slice(&self.dest.to_le_bytes());
98 b[16..20].copy_from_slice(&self.tag.to_le_bytes());
99 b[20..28].copy_from_slice(&self.count.to_le_bytes());
100 b[28..32].copy_from_slice(&self.datatype.to_le_bytes());
101 b[32..40].copy_from_slice(&self.len.to_le_bytes());
102 b
103 }
104
105 fn from_bytes(b: &[u8; HEADER_LEN]) -> io::Result<Header> {
106 let magic = u32::from_le_bytes(b[0..4].try_into().unwrap());
107 if magic != MAGIC {
108 return Err(io::Error::new(
109 io::ErrorKind::InvalidData,
110 "bad MPI wire magic",
111 ));
112 }
113 Ok(Header {
114 comm: u32::from_le_bytes(b[4..8].try_into().unwrap()),
115 source: i32::from_le_bytes(b[8..12].try_into().unwrap()),
116 dest: i32::from_le_bytes(b[12..16].try_into().unwrap()),
117 tag: i32::from_le_bytes(b[16..20].try_into().unwrap()),
118 count: u64::from_le_bytes(b[20..28].try_into().unwrap()),
119 datatype: u32::from_le_bytes(b[28..32].try_into().unwrap()),
120 len: u64::from_le_bytes(b[32..40].try_into().unwrap()),
121 })
122 }
123}
124
125pub type Handler = Arc<dyn Fn(i32, i32, u64, u32, Vec<u8>) + Send + Sync>;
129
130struct Inbox {
134 queue: Mutex<VecDeque<Incoming>>,
135 cvar: Condvar,
136 handlers: Mutex<HashMap<u32, Handler>>,
137}
138
139impl Inbox {
140 fn new() -> Inbox {
141 Inbox {
142 queue: Mutex::new(VecDeque::new()),
143 cvar: Condvar::new(),
144 handlers: Mutex::new(HashMap::new()),
145 }
146 }
147
148 fn push(&self, msg: Incoming) {
149 let handler = self.handlers.lock().unwrap().get(&msg.comm).cloned();
152 if let Some(h) = handler {
153 h(msg.source, msg.tag, msg.count, msg.datatype, msg.payload);
154 return;
155 }
156 let mut q = self.queue.lock().unwrap();
157 q.push_back(msg);
158 self.cvar.notify_all();
159 }
160
161 fn matches(msg: &Incoming, comm: u32, source: i32, tag: i32) -> bool {
163 msg.comm == comm
164 && (source == ANY_SOURCE || msg.source == source)
165 && (tag == ANY_TAG || msg.tag == tag)
166 }
167
168 fn take_matching(&self, comm: u32, source: i32, tag: i32) -> Incoming {
173 let mut q = self.queue.lock().unwrap();
174 let mut waited = Duration::ZERO;
175 let mut warned = false;
176 loop {
177 if let Some(pos) = q.iter().position(|m| Inbox::matches(m, comm, source, tag)) {
178 return q.remove(pos).unwrap();
179 }
180 let (guard, res) = self.cvar.wait_timeout(q, Duration::from_secs(5)).unwrap();
181 q = guard;
182 if res.timed_out() {
183 waited += Duration::from_secs(5);
184 if waited >= Duration::from_secs(30) && !warned {
185 warned = true;
186 let rank = RUNTIME.get().map(|r| r.rank).unwrap_or(-1);
187 eprintln!(
188 "[mpi] rank {rank} has been blocked for {}s in receive \
189 (comm ctx {comm}, source {source}, tag {tag}) — possible deadlock",
190 waited.as_secs()
191 );
192 }
193 }
194 }
195 }
196
197 fn peek_matching(
200 &self,
201 comm: u32,
202 source: i32,
203 tag: i32,
204 ) -> Option<(i32, i32, u64, u32, usize)> {
205 let q = self.queue.lock().unwrap();
206 q.iter()
207 .find(|m| Inbox::matches(m, comm, source, tag))
208 .map(|m| (m.source, m.tag, m.count, m.datatype, m.payload.len()))
209 }
210}
211
212pub struct Runtime {
214 pub rank: i32,
215 pub size: i32,
216 pub threading: crate::environment::Threading,
217 my_addr: Option<SocketAddr>,
219 addresses: Vec<SocketAddr>,
221 inbox: Arc<Inbox>,
222 outgoing: Mutex<HashMap<SocketAddr, Arc<Mutex<TcpStream>>>>,
226 context_peers: Mutex<HashMap<u32, Vec<SocketAddr>>>,
231 buffer_size: Mutex<usize>,
233 #[cfg(feature = "shm")]
235 shm: Option<crate::shm::ShmTransport>,
236}
237
238static RUNTIME: OnceLock<Runtime> = OnceLock::new();
239
240static SPAWN_PARENT: Mutex<Option<ParentBlock>> = Mutex::new(None);
244
245pub fn spawn_parent() -> Option<ParentBlock> {
247 SPAWN_PARENT.lock().unwrap().clone()
248}
249
250pub fn abort_job(code: i32) -> ! {
254 if !ABORTING.swap(true, Ordering::SeqCst) {
256 if let Some(rt) = RUNTIME.get() {
257 if std::env::var("MPI_DEBUG").is_ok() {
258 eprintln!(
259 "[abort] rank {} broadcasting abort {code} to {} peers",
260 rt.rank,
261 rt.size - 1
262 );
263 }
264 let header = Header {
265 comm: ABORT_CONTEXT,
266 source: rt.rank,
267 dest: 0,
268 tag: 0,
269 count: 1,
270 datatype: crate::datatype::ids::I32,
271 len: 4,
272 }
273 .to_bytes();
274 let code_bytes = code.to_le_bytes();
275 for w in 0..rt.size {
276 if w == rt.rank {
277 continue;
278 }
279 if let Some(addr) = rt.peer_addr(w) {
280 match TcpStream::connect_timeout(&addr, Duration::from_millis(300)) {
282 Ok(mut s) => {
283 let _ = s.write_all(&header);
284 let _ = s.write_all(&code_bytes);
285 let _ = s.flush();
286 if std::env::var("MPI_DEBUG").is_ok() {
287 eprintln!("[abort] sent to rank {w} at {addr}");
288 }
289 }
290 Err(e) => {
291 if std::env::var("MPI_DEBUG").is_ok() {
292 eprintln!("[abort] connect to rank {w} at {addr} failed: {e}");
293 }
294 }
295 }
296 }
297 }
298 }
299 }
300 std::process::exit(code);
301}
302
303fn install_fault_handling() {
306 runtime().register_handler(
308 ABORT_CONTEXT,
309 Arc::new(|_src, _tag, _count, _dt, payload: Vec<u8>| {
310 let code = if payload.len() >= 4 {
311 i32::from_le_bytes(payload[..4].try_into().unwrap())
312 } else {
313 1
314 };
315 if !ABORTING.swap(true, Ordering::SeqCst) {
316 eprintln!("MPI job aborting (peer signalled exit {code})");
317 }
318 std::process::exit(code);
319 }),
320 );
321
322 runtime().register_handler(
325 RNDV_CTS_CTEXT,
326 Arc::new(|_src, _tag, _count, _dt, payload: Vec<u8>| {
327 if payload.len() < 12 {
328 return;
329 }
330 let id = u64::from_le_bytes(payload[0..8].try_into().unwrap());
331 let requester = i32::from_le_bytes(payload[8..12].try_into().unwrap());
332 let data = RNDV_OUT
333 .lock()
334 .unwrap()
335 .as_mut()
336 .and_then(|m| m.remove(&id));
337 if let Some(data) = data {
338 let rt = runtime();
339 let id_tag = (id & 0x7FFF_FFFF) as i32;
340 let _ = rt.send_eager(
341 RNDV_DATA_CTEXT,
342 rt.rank,
343 requester,
344 id_tag,
345 data.len() as u64,
346 crate::datatype::ids::U8,
347 &data,
348 );
349 }
350 }),
351 );
352
353 let default_hook = std::panic::take_hook();
355 std::panic::set_hook(Box::new(move |info| {
356 default_hook(info);
357 abort_job(101);
358 }));
359}
360
361#[allow(dead_code)]
363pub fn is_initialized() -> bool {
364 RUNTIME.get().is_some()
365}
366
367pub fn runtime() -> &'static Runtime {
370 RUNTIME
371 .get()
372 .expect("MPI used before mpi::initialize() / after finalize")
373}
374
375#[cfg(target_endian = "big")]
380fn swap_elems(buf: &mut [u8], elem: usize) {
381 if elem > 1 {
382 for chunk in buf.chunks_exact_mut(elem) {
383 chunk.reverse();
384 }
385 }
386}
387
388#[cfg(target_endian = "big")]
392impl Runtime {
393 fn handler_registered(&self, comm: u32) -> bool {
394 self.inbox.handlers.lock().unwrap().contains_key(&comm)
395 }
396}
397
398fn detect_local_ip_toward(peer: &str) -> Option<String> {
402 let sock = std::net::UdpSocket::bind("0.0.0.0:0").ok()?;
403 sock.connect(peer).ok()?;
404 Some(sock.local_addr().ok()?.ip().to_string())
405}
406
407pub fn init(threading: crate::environment::Threading) -> Result<(), crate::MpiError> {
409 if RUNTIME.get().is_some() {
410 return Err(crate::MpiError::AlreadyInitialized);
411 }
412
413 let inbox = Arc::new(Inbox::new());
414
415 let pmi = std::env::var("MPI_PMI_ROOT").ok();
417 let (rank, size, addresses, my_addr) = match pmi {
418 Some(root_addr) => {
419 let rank: i32 = std::env::var("MPI_PMI_RANK")
420 .map_err(|_| crate::MpiError::Bootstrap("MPI_PMI_RANK missing".into()))?
421 .parse()
422 .map_err(|_| crate::MpiError::Bootstrap("MPI_PMI_RANK invalid".into()))?;
423 let size: i32 = std::env::var("MPI_PMI_SIZE")
424 .map_err(|_| crate::MpiError::Bootstrap("MPI_PMI_SIZE missing".into()))?
425 .parse()
426 .map_err(|_| crate::MpiError::Bootstrap("MPI_PMI_SIZE invalid".into()))?;
427
428 let advertise_ip = std::env::var("MPI_HOST_IP").ok().or_else(|| {
435 if std::env::var("MPI_MULTIHOST").is_ok() {
436 detect_local_ip_toward(&root_addr)
437 } else {
438 None
439 }
440 });
441 let bind_host: &str = if advertise_ip.is_some() {
442 "0.0.0.0"
443 } else {
444 "127.0.0.1"
445 };
446 let listener = TcpListener::bind((bind_host, 0))
447 .map_err(|e| crate::MpiError::Bootstrap(format!("bind failed: {e}")))?;
448 let port = listener
449 .local_addr()
450 .map_err(|e| crate::MpiError::Bootstrap(format!("local_addr failed: {e}")))?
451 .port();
452 let ip = advertise_ip.unwrap_or_else(|| "127.0.0.1".to_string());
453 let my_addr: SocketAddr = format!("{ip}:{port}")
454 .parse()
455 .map_err(|e| crate::MpiError::Bootstrap(format!("bad advertise addr: {e}")))?;
456
457 if std::env::var("MPI_DEBUG").is_ok() {
458 eprintln!(
459 "[mpi rank {rank}] multihost={} advertise={my_addr} root={root_addr}",
460 std::env::var("MPI_MULTIHOST").is_ok()
461 );
462 }
463 let (addresses, parent) = pmi_exchange(&root_addr, rank, size, my_addr)?;
464 if let Some(p) = parent {
465 *SPAWN_PARENT.lock().unwrap() = Some(p);
466 }
467
468 spawn_acceptor(listener, Arc::clone(&inbox));
469
470 (rank, size, addresses, Some(my_addr))
471 }
472 None => {
473 let listener = TcpListener::bind(("127.0.0.1", 0))
476 .map_err(|e| crate::MpiError::Bootstrap(format!("bind failed: {e}")))?;
477 let port = listener.local_addr().map(|a| a.port()).unwrap_or(0);
478 let my_addr: SocketAddr = format!("127.0.0.1:{port}")
479 .parse()
480 .map_err(|e| crate::MpiError::Bootstrap(format!("bad addr: {e}")))?;
481 spawn_acceptor(listener, Arc::clone(&inbox));
482 (0, 1, vec![my_addr], Some(my_addr))
483 }
484 };
485
486 #[cfg(feature = "shm")]
488 let shm = {
489 let jobid: u64 = std::env::var("MPI_JOBID")
490 .ok()
491 .and_then(|s| s.parse().ok())
492 .unwrap_or_else(|| std::process::id() as u64);
493 let same_host: Vec<i32> = match my_addr {
494 Some(me) => (0..size)
495 .filter(|&w| {
496 w != rank
497 && addresses
498 .get(w as usize)
499 .map(|a| a.ip() == me.ip())
500 .unwrap_or(false)
501 })
502 .collect(),
503 None => Vec::new(),
504 };
505 let inbox_shm = Arc::clone(&inbox);
506 let on_recv: crate::shm::OnRecv = Arc::new(move |framed: Vec<u8>| {
507 if framed.len() >= HEADER_LEN {
508 let arr: [u8; HEADER_LEN] = framed[..HEADER_LEN].try_into().unwrap();
509 if let Ok(h) = Header::from_bytes(&arr) {
510 inbox_shm.push(Incoming {
511 comm: h.comm,
512 source: h.source,
513 tag: h.tag,
514 count: h.count,
515 datatype: h.datatype,
516 payload: framed[HEADER_LEN..].to_vec(),
517 });
518 }
519 }
520 });
521 crate::shm::ShmTransport::init(jobid, rank, &same_host, on_recv)
522 };
523
524 let rt = Runtime {
525 rank,
526 size,
527 threading,
528 my_addr,
529 addresses,
530 inbox,
531 outgoing: Mutex::new(HashMap::new()),
532 context_peers: Mutex::new(HashMap::new()),
533 buffer_size: Mutex::new(0),
534 #[cfg(feature = "shm")]
535 shm,
536 };
537
538 RUNTIME
539 .set(rt)
540 .map_err(|_| crate::MpiError::AlreadyInitialized)?;
541 install_fault_handling();
542 Ok(())
543}
544
545fn pmi_exchange(
548 root_addr: &str,
549 rank: i32,
550 size: i32,
551 my_addr: SocketAddr,
552) -> Result<(Vec<SocketAddr>, Option<ParentBlock>), crate::MpiError> {
553 let mut stream = TcpStream::connect(root_addr)
554 .map_err(|e| crate::MpiError::Bootstrap(format!("connect to launcher failed: {e}")))?;
555 let line = format!("{} {} {}\n", rank, my_addr, MY_ENDIAN);
556 stream
557 .write_all(line.as_bytes())
558 .map_err(|e| crate::MpiError::Bootstrap(format!("PMI write failed: {e}")))?;
559
560 let mut buf = String::new();
562 let mut reader = io::BufReader::new(stream);
563 use std::io::BufRead;
564 let mut table: Vec<Option<SocketAddr>> = vec![None; size as usize];
565 let mut endians: Vec<String> = vec![MY_ENDIAN.to_string(); size as usize];
566 for _ in 0..size {
567 buf.clear();
568 let n = reader
569 .read_line(&mut buf)
570 .map_err(|e| crate::MpiError::Bootstrap(format!("PMI read failed: {e}")))?;
571 if n == 0 {
572 return Err(crate::MpiError::Bootstrap(
573 "launcher closed connection early".into(),
574 ));
575 }
576 let mut parts = buf.split_whitespace();
577 let r: usize = parts
578 .next()
579 .and_then(|s| s.parse().ok())
580 .ok_or_else(|| crate::MpiError::Bootstrap("bad PMI table entry".into()))?;
581 let a: SocketAddr = parts
582 .next()
583 .and_then(|s| s.parse().ok())
584 .ok_or_else(|| crate::MpiError::Bootstrap("bad PMI table addr".into()))?;
585 table[r] = Some(a);
586 if let Some(e) = parts.next() {
587 endians[r] = e.to_string();
588 }
589 }
590
591 if rank == 0 {
596 if let Some(bad) = endians.iter().position(|e| e != MY_ENDIAN) {
597 eprintln!(
598 "[mpi] warning: mixed-endian job (rank 0 is {MY_ENDIAN}, rank {bad} is {}); \
599 point-to-point/collectives are transcoded, but RMA windows and \
600 #[derive(Equivalence)] structs require same-endian ranks",
601 endians[bad]
602 );
603 }
604 }
605
606 let mut addresses = Vec::with_capacity(size as usize);
607 for (r, a) in table.into_iter().enumerate() {
608 addresses.push(
609 a.ok_or_else(|| crate::MpiError::Bootstrap(format!("missing address for rank {r}")))?,
610 );
611 }
612
613 let mut parent = None;
616 if std::env::var("MPI_SPAWN").is_ok() {
617 buf.clear();
618 if reader.read_line(&mut buf).unwrap_or(0) > 0 {
619 let mut it = buf.split_whitespace();
620 if it.next() == Some("PARENT") {
621 let ictx: u32 = it.next().and_then(|s| s.parse().ok()).unwrap_or(0);
622 let count: usize = it.next().and_then(|s| s.parse().ok()).unwrap_or(0);
623 let mut paddrs = Vec::with_capacity(count);
624 for _ in 0..count {
625 buf.clear();
626 reader.read_line(&mut buf).ok();
627 if let Ok(a) = buf.trim().parse::<SocketAddr>() {
628 paddrs.push(a);
629 }
630 }
631 parent = Some((ictx, paddrs));
632 }
633 }
634 }
635
636 Ok((addresses, parent))
637}
638
639fn spawn_acceptor(listener: TcpListener, inbox: Arc<Inbox>) {
641 thread::spawn(move || {
642 for stream in listener.incoming() {
643 match stream {
644 Ok(s) => {
645 let inbox = Arc::clone(&inbox);
646 thread::spawn(move || reader_loop(s, inbox));
647 }
648 Err(_) => break,
649 }
650 }
651 });
652}
653
654fn reader_loop(mut stream: TcpStream, inbox: Arc<Inbox>) {
656 let _ = stream.set_nodelay(true);
657 loop {
658 let mut hdr = [0u8; HEADER_LEN];
659 if stream.read_exact(&mut hdr).is_err() {
660 return;
661 }
662 let header = match Header::from_bytes(&hdr) {
663 Ok(h) => h,
664 Err(_) => return,
665 };
666 let mut payload = vec![0u8; header.len as usize];
667 if stream.read_exact(&mut payload).is_err() {
668 return;
669 }
670 inbox.push(Incoming {
671 comm: header.comm,
672 source: header.source,
673 tag: header.tag,
674 count: header.count,
675 datatype: header.datatype,
676 payload,
677 });
678 }
679}
680
681impl Runtime {
682 fn resolve(&self, comm: u32, dest: i32) -> Option<SocketAddr> {
686 if let Some(peers) = self.context_peers.lock().unwrap().get(&comm) {
687 return peers.get(dest as usize).copied();
688 }
689 self.addresses.get(dest as usize).copied()
690 }
691
692 fn has_override(&self, comm: u32) -> bool {
694 self.context_peers.lock().unwrap().contains_key(&comm)
695 }
696
697 pub fn register_context_peers(&self, ctx: u32, peers: Vec<SocketAddr>) {
699 self.context_peers.lock().unwrap().insert(ctx, peers);
700 }
701
702 #[allow(dead_code)]
704 pub fn unregister_context_peers(&self, ctx: u32) {
705 self.context_peers.lock().unwrap().remove(&ctx);
706 }
707
708 pub fn peer_addr(&self, world_rank: i32) -> Option<SocketAddr> {
710 self.addresses.get(world_rank as usize).copied()
711 }
712
713 pub fn my_addr(&self) -> Option<SocketAddr> {
715 self.my_addr
716 }
717
718 fn connection(&self, addr: SocketAddr) -> io::Result<Arc<Mutex<TcpStream>>> {
720 {
721 let map = self.outgoing.lock().unwrap();
722 if let Some(c) = map.get(&addr) {
723 return Ok(Arc::clone(c));
724 }
725 }
726 let stream = {
729 let mut attempt = 0u32;
730 loop {
731 match TcpStream::connect(addr) {
732 Ok(s) => break s,
733 Err(_) if attempt < 250 => {
734 attempt += 1;
735 std::thread::sleep(std::time::Duration::from_millis(20));
736 }
737 Err(e) => return Err(e),
738 }
739 }
740 };
741 stream.set_nodelay(true).ok();
742 let conn = Arc::new(Mutex::new(stream));
743 let mut map = self.outgoing.lock().unwrap();
744 let entry = map.entry(addr).or_insert_with(|| Arc::clone(&conn));
746 Ok(Arc::clone(entry))
747 }
748
749 #[allow(clippy::too_many_arguments)]
760 pub fn send(
761 &self,
762 comm: u32,
763 src: i32,
764 dest_world: i32,
765 tag: i32,
766 count: u64,
767 datatype: u32,
768 payload: &[u8],
769 ) -> io::Result<()> {
770 let normal_ctx = comm != ABORT_CONTEXT && comm != RNDV_CTS_CTEXT && comm != RNDV_DATA_CTEXT;
771 if payload.len() > RNDV_THRESHOLD
772 && normal_ctx
773 && !self.has_override(comm)
774 && dest_world != self.rank
775 {
776 return self.send_rendezvous(comm, src, dest_world, tag, count, datatype, payload);
777 }
778 self.send_eager(comm, src, dest_world, tag, count, datatype, payload)
779 }
780
781 #[allow(clippy::too_many_arguments)]
783 fn send_rendezvous(
784 &self,
785 comm: u32,
786 src: i32,
787 dest_world: i32,
788 tag: i32,
789 count: u64,
790 datatype: u32,
791 payload: &[u8],
792 ) -> io::Result<()> {
793 let id = ((self.rank as u64) << 40)
794 | (RNDV_SEQ.fetch_add(1, Ordering::Relaxed) & 0xFF_FFFF_FFFF);
795 let stored = payload.to_vec();
797 #[cfg(target_endian = "big")]
798 let stored = {
799 let mut v = stored;
800 swap_elems(&mut v, crate::datatype::wire_elem_size(datatype));
801 v
802 };
803 RNDV_OUT
804 .lock()
805 .unwrap()
806 .get_or_insert_with(HashMap::new)
807 .insert(id, stored);
808 let mut rts = Vec::with_capacity(20);
810 rts.extend_from_slice(&id.to_le_bytes());
811 rts.extend_from_slice(&self.rank.to_le_bytes());
812 rts.extend_from_slice(&(payload.len() as u64).to_le_bytes());
813 self.send_eager(comm, src, dest_world, tag, count, datatype | RTS_BIT, &rts)
814 }
815
816 #[allow(clippy::too_many_arguments)]
817 fn send_eager(
818 &self,
819 comm: u32,
820 src: i32,
821 dest_world: i32,
822 tag: i32,
823 count: u64,
824 datatype: u32,
825 payload: &[u8],
826 ) -> io::Result<()> {
827 if !self.has_override(comm) && dest_world == self.rank {
830 self.inbox.push(Incoming {
831 comm,
832 source: src,
833 tag,
834 count,
835 datatype,
836 payload: payload.to_vec(),
837 });
838 return Ok(());
839 }
840 let addr = self.resolve(comm, dest_world).ok_or_else(|| {
841 io::Error::new(io::ErrorKind::NotFound, "no address for destination rank")
842 })?;
843 if Some(addr) == self.my_addr {
846 self.inbox.push(Incoming {
847 comm,
848 source: src,
849 tag,
850 count,
851 datatype,
852 payload: payload.to_vec(),
853 });
854 return Ok(());
855 }
856 #[cfg(target_endian = "big")]
860 let swapped: Option<Vec<u8>> = {
861 let esz = crate::datatype::wire_elem_size(datatype);
862 if datatype & RTS_BIT == 0 && esz > 1 && !self.handler_registered(comm) {
863 let mut v = payload.to_vec();
864 swap_elems(&mut v, esz);
865 Some(v)
866 } else {
867 None
868 }
869 };
870 #[cfg(target_endian = "big")]
871 let payload: &[u8] = swapped.as_deref().unwrap_or(payload);
872
873 let header = Header {
874 comm,
875 source: src,
876 dest: dest_world,
877 tag,
878 count,
879 datatype,
880 len: payload.len() as u64,
881 };
882 #[cfg(feature = "shm")]
884 {
885 if !self.has_override(comm) {
886 if let Some(shm) = &self.shm {
887 let mut framed = Vec::with_capacity(HEADER_LEN + payload.len());
888 framed.extend_from_slice(&header.to_bytes());
889 framed.extend_from_slice(payload);
890 if shm.try_send(dest_world, &framed) {
891 return Ok(());
892 }
893 }
894 }
895 }
896 let conn = self.connection(addr)?;
897 let mut s = conn.lock().unwrap();
898 s.write_all(&header.to_bytes())?;
899 s.write_all(payload)?;
900 s.flush()?;
901 Ok(())
902 }
903
904 pub fn recv(&self, comm: u32, source: i32, tag: i32) -> (i32, i32, u64, u32, Vec<u8>) {
908 let m = self.inbox.take_matching(comm, source, tag);
909 if m.datatype & RTS_BIT != 0 {
910 let id = u64::from_le_bytes(m.payload[0..8].try_into().unwrap());
912 let sender_world = i32::from_le_bytes(m.payload[8..12].try_into().unwrap());
913 let id_tag = (id & 0x7FFF_FFFF) as i32;
914 let mut cts = Vec::with_capacity(12);
915 cts.extend_from_slice(&id.to_le_bytes());
916 cts.extend_from_slice(&self.rank.to_le_bytes());
917 let _ = self.send_eager(
918 RNDV_CTS_CTEXT,
919 self.rank,
920 sender_world,
921 0,
922 1,
923 crate::datatype::ids::U8,
924 &cts,
925 );
926 let data = self
927 .inbox
928 .take_matching(RNDV_DATA_CTEXT, sender_world, id_tag);
929 let real_dt = m.datatype & !RTS_BIT;
930 let payload = data.payload;
931 #[cfg(target_endian = "big")]
933 let payload = {
934 let mut v = payload;
935 swap_elems(&mut v, crate::datatype::wire_elem_size(real_dt));
936 v
937 };
938 return (m.source, m.tag, m.count, real_dt, payload);
939 }
940 let payload = m.payload;
942 #[cfg(target_endian = "big")]
943 let payload = {
944 let mut v = payload;
945 swap_elems(&mut v, crate::datatype::wire_elem_size(m.datatype));
946 v
947 };
948 (m.source, m.tag, m.count, m.datatype, payload)
949 }
950
951 pub fn probe(&self, comm: u32, source: i32, tag: i32) -> Option<(i32, i32, u64, u32, usize)> {
953 self.inbox.peek_matching(comm, source, tag)
954 }
955
956 pub fn probe_blocking(&self, comm: u32, source: i32, tag: i32) -> (i32, i32, u64, u32, usize) {
959 loop {
960 if let Some(info) = self.probe(comm, source, tag) {
961 return info;
962 }
963 let q = self.inbox.queue.lock().unwrap();
965 let _unused = self.inbox.cvar.wait(q).unwrap();
966 }
967 }
968
969 pub fn buffer_attach(&self, size: usize) {
971 *self.buffer_size.lock().unwrap() += size;
972 }
973
974 pub fn buffer_detach(&self) -> usize {
976 let mut b = self.buffer_size.lock().unwrap();
977 std::mem::replace(&mut *b, 0)
978 }
979
980 pub fn register_handler(&self, ctx: u32, handler: Handler) {
982 self.inbox.handlers.lock().unwrap().insert(ctx, handler);
983 }
984
985 pub fn unregister_handler(&self, ctx: u32) {
987 self.inbox.handlers.lock().unwrap().remove(&ctx);
988 }
989
990 pub fn buffer_size(&self) -> usize {
992 *self.buffer_size.lock().unwrap()
993 }
994
995 pub fn set_buffer_size(&self, size: usize) {
997 *self.buffer_size.lock().unwrap() = size;
998 }
999}