#![warn(missing_docs)]
#![warn(clippy::all)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_possible_wrap)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::similar_names)]
use std::ffi::c_char;
mod comm;
mod datatype;
mod error;
mod ffi;
mod info;
mod persistent;
mod request;
#[cfg(feature = "numa")]
pub mod slurm;
mod status;
#[cfg(feature = "rma")]
mod window;
pub use comm::{Communicator, SplitType};
pub use datatype::{DatatypeTag, MpiDatatype};
pub use error::{Error, MpiErrorClass, Result};
pub use info::Info;
pub use persistent::PersistentRequest;
pub use request::Request;
pub use status::Status;
#[cfg(feature = "rma")]
pub use window::{LockAllGuard, LockGuard, LockType, SharedWindow};
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, Ordering};
static MPI_INITIALIZED: AtomicBool = AtomicBool::new(false);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(i32)]
pub enum ThreadLevel {
Single = 0,
Funneled = 1,
Serialized = 2,
Multiple = 3,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum ReduceOp {
Sum = 0,
Max = 1,
Min = 2,
Prod = 3,
}
pub struct Mpi {
thread_level: ThreadLevel,
_marker: PhantomData<*const ()>,
}
impl Mpi {
pub fn init() -> Result<Self> {
Self::init_thread(ThreadLevel::Single)
}
pub fn init_thread(required: ThreadLevel) -> Result<Self> {
if MPI_INITIALIZED.swap(true, Ordering::SeqCst) {
return Err(Error::AlreadyInitialized);
}
let mut provided: i32 = 0;
let ret = unsafe { ffi::ferrompi_init_thread(required as i32, &mut provided) };
if ret != 0 {
MPI_INITIALIZED.store(false, Ordering::SeqCst);
return Err(Error::from_code(ret));
}
let thread_level = match provided {
0 => ThreadLevel::Single,
1 => ThreadLevel::Funneled,
2 => ThreadLevel::Serialized,
_ => ThreadLevel::Multiple,
};
Ok(Mpi {
thread_level,
_marker: PhantomData,
})
}
pub fn thread_level(&self) -> ThreadLevel {
self.thread_level
}
pub fn world(&self) -> Communicator {
Communicator::world()
}
pub fn wtime() -> f64 {
unsafe { ffi::ferrompi_wtime() }
}
pub fn version() -> Result<String> {
let mut buf = [0u8; 256];
let mut len: i32 = 0;
let ret = unsafe { ffi::ferrompi_get_version(buf.as_mut_ptr().cast::<c_char>(), &mut len) };
if ret != 0 {
return Err(Error::from_code(ret));
}
let len = len.max(0) as usize;
let s = std::str::from_utf8(&buf[..len])
.map_err(|_| Error::Internal("Invalid UTF-8 in version string".into()))?;
Ok(s.to_string())
}
pub fn is_initialized() -> bool {
let mut flag: i32 = 0;
unsafe { ffi::ferrompi_initialized(&mut flag) };
flag != 0
}
pub fn is_finalized() -> bool {
let mut flag: i32 = 0;
unsafe { ffi::ferrompi_finalized(&mut flag) };
flag != 0
}
}
impl Drop for Mpi {
fn drop(&mut self) {
if MPI_INITIALIZED.load(Ordering::SeqCst) {
unsafe {
ffi::ferrompi_finalize();
}
MPI_INITIALIZED.store(false, Ordering::SeqCst);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn thread_level_ordering() {
assert!(ThreadLevel::Single < ThreadLevel::Funneled);
assert!(ThreadLevel::Funneled < ThreadLevel::Serialized);
assert!(ThreadLevel::Serialized < ThreadLevel::Multiple);
assert!(ThreadLevel::Single < ThreadLevel::Multiple);
}
#[test]
fn thread_level_equality() {
assert_eq!(ThreadLevel::Single, ThreadLevel::Single);
assert_eq!(ThreadLevel::Funneled, ThreadLevel::Funneled);
assert_eq!(ThreadLevel::Serialized, ThreadLevel::Serialized);
assert_eq!(ThreadLevel::Multiple, ThreadLevel::Multiple);
assert_ne!(ThreadLevel::Single, ThreadLevel::Multiple);
assert_ne!(ThreadLevel::Funneled, ThreadLevel::Serialized);
}
#[test]
fn thread_level_repr_values() {
assert_eq!(ThreadLevel::Single as i32, 0);
assert_eq!(ThreadLevel::Funneled as i32, 1);
assert_eq!(ThreadLevel::Serialized as i32, 2);
assert_eq!(ThreadLevel::Multiple as i32, 3);
}
#[test]
fn thread_level_debug_clone() {
let level = ThreadLevel::Funneled;
let cloned = level;
assert_eq!(format!("{cloned:?}"), "Funneled");
assert_eq!(format!("{:?}", ThreadLevel::Single), "Single");
assert_eq!(format!("{:?}", ThreadLevel::Serialized), "Serialized");
assert_eq!(format!("{:?}", ThreadLevel::Multiple), "Multiple");
}
#[test]
fn reduce_op_repr_values() {
assert_eq!(ReduceOp::Sum as i32, 0);
assert_eq!(ReduceOp::Max as i32, 1);
assert_eq!(ReduceOp::Min as i32, 2);
assert_eq!(ReduceOp::Prod as i32, 3);
}
#[test]
fn reduce_op_equality() {
assert_eq!(ReduceOp::Sum, ReduceOp::Sum);
assert_eq!(ReduceOp::Max, ReduceOp::Max);
assert_eq!(ReduceOp::Min, ReduceOp::Min);
assert_eq!(ReduceOp::Prod, ReduceOp::Prod);
assert_ne!(ReduceOp::Sum, ReduceOp::Max);
assert_ne!(ReduceOp::Min, ReduceOp::Prod);
assert_ne!(ReduceOp::Sum, ReduceOp::Prod);
}
#[test]
fn reduce_op_debug_clone() {
let op = ReduceOp::Sum;
let cloned = op;
assert_eq!(format!("{cloned:?}"), "Sum");
assert_eq!(format!("{:?}", ReduceOp::Max), "Max");
assert_eq!(format!("{:?}", ReduceOp::Min), "Min");
assert_eq!(format!("{:?}", ReduceOp::Prod), "Prod");
}
}