mpi/attribute.rs
1//! Attribute caching on communicators
2
3use std::{any::TypeId, collections::HashMap, ffi::c_void, os::raw::c_int, ptr, sync::RwLock};
4
5use once_cell::sync::Lazy;
6
7use crate::{ffi, traits::AsRaw};
8
9/// Topology traits
10pub mod traits {
11 pub use super::CommAttribute;
12}
13
14pub(crate) static COMM_ATTRS: Lazy<RwLock<HashMap<TypeId, AttributeKey>>> =
15 Lazy::new(|| RwLock::new(HashMap::new()));
16
17/// Attributes are user data that can be owned by communicators and accessed by
18/// users. They are useful when libraries pass communicators to a different
19/// library and get it back in a callback.
20///
21/// # Standard section(s)
22///
23/// 7.7.1
24pub trait CommAttribute
25where
26 Self: 'static + Sized + Clone,
27{
28 /// When a communicator is duplicated, attributes can either be cloned to
29 /// the new communicator or not propagated at all. Implementations of
30 /// CommAttribute can determine this behavior by defining this associated
31 /// constant. The default does not propagated attributes to cloned
32 /// communicators.
33 const CLONE_ON_DUP: bool = false;
34
35 /// Callback invoked by `MPI Comm_free`, `MPI_Comm_disconnect`, and
36 /// `MPI_Comm_delete_attr` to delete an attribute.
37 ///
38 /// User-defined attributes should not need to override this default
39 /// implementation.
40 ///
41 /// # Safety
42 ///
43 /// This default implementation goes with the boxing in
44 /// `AnyCommunicator::set_attr()`.
45 unsafe extern "C" fn comm_delete_attr_fn(
46 _comm: ffi::MPI_Comm,
47 _key: c_int,
48 val: *mut c_void,
49 _extra_state: *mut c_void,
50 ) -> c_int {
51 let _to_drop = Box::from_raw(val as *mut Self);
52 ffi::MPI_SUCCESS as i32
53 }
54
55 /// Callback invoked by `MPI_Comm_dup()`, `MPI_Comm_idup()`, and variants to
56 /// (optionally) clone the attribute to the new communicator. The behavior
57 /// of this function is determined by `Self::CLONE_ON_DUP`, which should be
58 /// sufficient to obtain desired semantics.
59 ///
60 /// User-defined attributes should not need to override this default
61 /// implementation.
62 ///
63 /// # Safety
64 ///
65 /// This default implementation must only be used with boxed attributes, as
66 /// in `AnyCommunicator::set_attr()`.
67 unsafe extern "C" fn comm_copy_attr_fn(
68 _old_comm: ffi::MPI_Comm,
69 _key: c_int,
70 _extra_state: *mut c_void,
71 val_in: *mut c_void,
72 val_out: *mut c_void,
73 flag: *mut c_int,
74 ) -> c_int {
75 if Self::CLONE_ON_DUP {
76 let b_in = Box::from_raw(val_in as *mut Self);
77 let b_out = b_in.clone();
78 *(val_out as *mut *mut Self) = Box::into_raw(b_out);
79 Box::into_raw(b_in); // deconstruct to avoid dropping
80 *flag = 1;
81 } else {
82 *flag = 0;
83 }
84 ffi::MPI_SUCCESS as i32
85 }
86
87 /// Get the attribute key for this attribute. User keys are provisioned by
88 /// `MPI_Comm_create_keyval()`, which we store in the universe and free
89 /// prior to `MPI_Finalize()` when the universe is dropped.
90 ///
91 /// In multi-language projects for which `MPI_Comm_create_keyval()` is
92 /// called from a different language, the attribute can be set and retrieved
93 /// in Rust by overriding this default implementation to return the foreign
94 /// key.
95 fn get_key() -> AttributeKey {
96 let id = TypeId::of::<Self>();
97 {
98 let comm_attrs = COMM_ATTRS.read().expect("COMM_ATTRS RwLock poisoned");
99 if let Some(key) = comm_attrs.get(&id) {
100 return key.clone();
101 }
102 }
103 let mut key: i32 = 0;
104 unsafe {
105 ffi::MPI_Comm_create_keyval(
106 Some(Self::comm_copy_attr_fn),
107 Some(Self::comm_delete_attr_fn),
108 &mut key,
109 ptr::null_mut(),
110 );
111 }
112 let key = AttributeKey(key);
113 let mut comm_attrs = COMM_ATTRS.write().expect("COMM_ATTRS RwLock poisoned");
114 comm_attrs.insert(id, key.clone());
115 key
116 }
117}
118
119/// Attribute keys are used internally to access attributes. They are obtained
120/// with the associated function `CommAttribute::get_key()`.
121///
122/// User keys are created with `MPI_Comm_create_keyval()` and should be freed
123/// with `MPI_Comm_free_keyval()`. They are provisioned in the default
124/// implementation of `CommAttribute::get_key()` and stored persistently in
125/// `COMM_ATTRS`.
126///
127/// System keys are automatically available and are not passed to
128/// `MPI_Comm_free_keyval()`.
129#[allow(missing_copy_implementations)]
130#[derive(Debug, Clone)]
131pub struct AttributeKey(i32);
132
133impl AttributeKey {
134 /// Create a new attribute key. This is mostly unnecessary for users, but
135 /// may be used when implementing `CommAttribute::get_key()` to be used with
136 /// a foreign keyval.
137 ///
138 /// # Safety
139 ///
140 /// The key must be a valid predefined system keyval or obtained by
141 /// `MPI_Comm_create_keyval()`. Strictly speaking, I think this can be safe,
142 /// but an invalid value will cause `MPI_Comm_get_attr()` and
143 /// `MPI_Comm_set_attr()` to fail. Note that they can also be made to fail
144 /// if `MPI_Comm_free_keyval()` is called after creating an `AttributeKey`
145 /// and before it is used. I'm leaving it `unsafe` to be defensive and
146 /// because the caller should think carefully about lifetime of their
147 /// foreign keyvals when interoperating with Rust.
148 pub unsafe fn new_unchecked(k: i32) -> Self {
149 Self(k)
150 }
151}
152
153unsafe impl AsRaw for AttributeKey {
154 type Raw = c_int;
155 fn as_raw(&self) -> Self::Raw {
156 self.0
157 }
158}
159
160/// For obtaining the universe size attribute
161#[repr(C)]
162#[derive(Clone)]
163pub(crate) struct UniverseSize(c_int);
164
165impl CommAttribute for UniverseSize {
166 fn get_key() -> AttributeKey {
167 unsafe { AttributeKey::new_unchecked(ffi::MPI_UNIVERSE_SIZE as i32) }
168 }
169}
170
171impl TryFrom<&UniverseSize> for usize {
172 type Error = std::num::TryFromIntError;
173 fn try_from(s: &UniverseSize) -> Result<Self, Self::Error> {
174 usize::try_from(s.0)
175 }
176}
177
178/// For obtaining the appnum attribute of MPI_COMM_WORLD
179#[repr(C)]
180#[derive(Clone)]
181pub(crate) struct AppNum(c_int);
182
183impl CommAttribute for AppNum {
184 fn get_key() -> AttributeKey {
185 unsafe { AttributeKey::new_unchecked(ffi::MPI_APPNUM as i32) }
186 }
187}
188
189impl From<&AppNum> for isize {
190 fn from(an: &AppNum) -> Self {
191 an.0 as isize
192 }
193}