use std::ptr::NonNull;
use crate::error::{Error, Result};
use crate::ffi;
use crate::Communicator;
use crate::MpiDatatype;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LockType {
Exclusive,
Shared,
}
pub struct SharedWindow<T: MpiDatatype> {
win_handle: i32,
local_ptr: NonNull<T>,
local_len: usize,
comm_size: i32,
}
impl<T: MpiDatatype> SharedWindow<T> {
pub fn allocate(comm: &Communicator, local_count: usize) -> Result<Self> {
let byte_size = local_count
.checked_mul(std::mem::size_of::<T>())
.ok_or(Error::InvalidBuffer)?;
let size = i64::try_from(byte_size).map_err(|_| Error::InvalidBuffer)?;
let disp_unit = std::mem::size_of::<T>() as i32;
let mut baseptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut win_handle: i32 = 0;
let ret = unsafe {
ffi::ferrompi_win_allocate_shared(
size,
disp_unit,
-1, comm.raw_handle(),
&mut baseptr,
&mut win_handle,
)
};
Error::check_with_op(ret, "win_allocate_shared")?;
let local_ptr = NonNull::new(baseptr.cast::<T>())
.ok_or_else(|| Error::Internal("Win_allocate_shared returned null".into()))?;
Ok(SharedWindow {
win_handle,
local_ptr,
local_len: local_count,
comm_size: comm.size(),
})
}
pub fn local_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.local_ptr.as_ptr(), self.local_len) }
}
pub fn local_slice_mut(&mut self) -> &mut [T] {
unsafe { std::slice::from_raw_parts_mut(self.local_ptr.as_ptr(), self.local_len) }
}
pub fn remote_slice(&self, rank: i32) -> Result<&[T]> {
let mut size: i64 = 0;
let mut disp_unit: i32 = 0;
let mut baseptr: *mut std::ffi::c_void = std::ptr::null_mut();
let ret = unsafe {
ffi::ferrompi_win_shared_query(
self.win_handle,
rank,
&mut size,
&mut disp_unit,
&mut baseptr,
)
};
Error::check_with_op(ret, "win_shared_query")?;
let count = size as usize / std::mem::size_of::<T>();
if baseptr.is_null() {
if count == 0 {
return Ok(unsafe {
std::slice::from_raw_parts(NonNull::<T>::dangling().as_ptr(), 0)
});
}
return Err(Error::Internal(
"MPI_Win_shared_query returned null for non-zero size".into(),
));
}
Ok(unsafe { std::slice::from_raw_parts(baseptr.cast::<T>(), count) })
}
pub fn fence(&self) -> Result<()> {
let ret = unsafe { ffi::ferrompi_win_fence(0, self.win_handle) };
Error::check_with_op(ret, "win_fence")
}
pub fn lock(&self, lock_type: LockType, rank: i32) -> Result<LockGuard<'_, T>> {
let lt = match lock_type {
LockType::Exclusive => ffi::FERROMPI_LOCK_EXCLUSIVE,
LockType::Shared => ffi::FERROMPI_LOCK_SHARED,
};
let ret = unsafe { ffi::ferrompi_win_lock(lt, rank, 0, self.win_handle) };
Error::check_with_op(ret, "win_lock")?;
Ok(LockGuard { window: self, rank })
}
pub fn lock_all(&self) -> Result<LockAllGuard<'_, T>> {
let ret = unsafe { ffi::ferrompi_win_lock_all(0, self.win_handle) };
Error::check_with_op(ret, "win_lock_all")?;
Ok(LockAllGuard { window: self })
}
pub fn raw_handle(&self) -> i32 {
self.win_handle
}
pub fn comm_size(&self) -> i32 {
self.comm_size
}
}
impl<T: MpiDatatype> Drop for SharedWindow<T> {
fn drop(&mut self) {
unsafe { ffi::ferrompi_win_free(self.win_handle) };
}
}
pub struct LockGuard<'a, T: MpiDatatype> {
window: &'a SharedWindow<T>,
rank: i32,
}
impl<T: MpiDatatype> LockGuard<'_, T> {
pub fn flush(&self) -> Result<()> {
let ret = unsafe { ffi::ferrompi_win_flush(self.rank, self.window.win_handle) };
Error::check_with_op(ret, "win_flush")
}
}
impl<T: MpiDatatype> Drop for LockGuard<'_, T> {
fn drop(&mut self) {
unsafe { ffi::ferrompi_win_unlock(self.rank, self.window.win_handle) };
}
}
pub struct LockAllGuard<'a, T: MpiDatatype> {
window: &'a SharedWindow<T>,
}
impl<T: MpiDatatype> LockAllGuard<'_, T> {
pub fn flush_all(&self) -> Result<()> {
let ret = unsafe { ffi::ferrompi_win_flush_all(self.window.win_handle) };
Error::check_with_op(ret, "win_flush_all")
}
pub fn flush(&self, rank: i32) -> Result<()> {
let ret = unsafe { ffi::ferrompi_win_flush(rank, self.window.win_handle) };
Error::check_with_op(ret, "win_flush")
}
}
impl<T: MpiDatatype> Drop for LockAllGuard<'_, T> {
fn drop(&mut self) {
unsafe { ffi::ferrompi_win_unlock_all(self.window.win_handle) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lock_type_equality() {
assert_eq!(LockType::Exclusive, LockType::Exclusive);
assert_eq!(LockType::Shared, LockType::Shared);
assert_ne!(LockType::Exclusive, LockType::Shared);
}
#[test]
fn lock_type_debug() {
assert_eq!(format!("{:?}", LockType::Exclusive), "Exclusive");
assert_eq!(format!("{:?}", LockType::Shared), "Shared");
}
#[test]
#[allow(clippy::clone_on_copy)]
fn lock_type_clone_copy() {
let original = LockType::Exclusive;
let copied = original; let cloned = original.clone(); assert_eq!(original, copied);
assert_eq!(original, cloned);
let original = LockType::Shared;
let copied = original; let cloned = original.clone(); assert_eq!(original, copied);
assert_eq!(original, cloned);
}
}