Skip to main content

lamellar/
memregion.rs

1//! Memory regions are unsafe low-level abstractions around shared memory segments that have been allocated by a lamellae provider.
2//!
3//! These memory region APIs provide the functionality to perform RDMA operations on the shared memory segments, and are at the core
4//! of how the Runtime communicates in a distributed environment (or using shared memory when using the `shmem` backend).
5//!
6//! # Warning
7//! This is a low-level module, unless you are very comfortable/confident in low level distributed memory (and even then) it is highly recommended you use the [LamellarArrays][crate::array] and [Active Messaging][crate::active_messaging] interfaces to perform distributed communications and computation.
8use crate::active_messaging::{AmDist, RemotePtr};
9use crate::array::{
10    LamellarArrayRdmaInput, LamellarArrayRdmaOutput, LamellarRead, LamellarWrite, TeamFrom,
11    TeamTryFrom,
12};
13use crate::lamellae::{AllocationType, Backend, Lamellae, LamellaeComm, LamellaeRDMA};
14use crate::lamellar_team::{LamellarTeam, LamellarTeamRT};
15use crate::LamellarEnv;
16use core::marker::PhantomData;
17use std::hash::{Hash, Hasher};
18use std::sync::Arc;
19
20//#[doc(hidden)]
21/// Prelude for using the [LamellarMemoryRegion] module
22pub mod prelude;
23
24pub(crate) mod shared;
25pub use shared::SharedMemoryRegion;
26
27pub(crate) mod one_sided;
28pub use one_sided::OneSidedMemoryRegion;
29
30pub(crate) mod handle;
31use handle::{FallibleSharedMemoryRegionHandle, SharedMemoryRegionHandle};
32
33use enum_dispatch::enum_dispatch;
34
35/// This error occurs when you are trying to directly access data locally on a PE through a memregion handle,
36/// but that PE does not contain any data for that memregion
37///
38/// This can occur when tryin to get the local data from a [OneSidedMemoryRegion] on any PE but the one which created it.
39///
40/// It can also occur if a subteam creates a shared memory region, and then a PE that does not exist in the team tries to access local data directly.
41///
42/// In both these cases the solution would be to use the memregion handle to perfrom a `get` operation, transferring the data from a remote node into a local buffer.
43#[derive(Debug, Clone)]
44pub struct MemNotLocalError;
45
46/// A Result type for LamellarMemoryRegion Operations
47pub type MemResult<T> = Result<T, MemNotLocalError>;
48
49impl std::fmt::Display for MemNotLocalError {
50    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
51        write!(f, "mem region not local",)
52    }
53}
54
55impl std::error::Error for MemNotLocalError {}
56
57/// Trait representing types that can be used in remote operations
58///
59/// as well as [Copy] so we can perform bitwise copies
60pub trait Dist:
61    AmDist + Sync + Send + Copy + serde::ser::Serialize + serde::de::DeserializeOwned + 'static
62// AmDist + Copy
63{
64}
65// impl<T: Send  + Copy + std::fmt::Debug + 'static>
66//     Dist for T
67// {
68// }
69
70//#[doc(hidden)]
71/// Enum used to expose common methods for all registered memory regions
72#[enum_dispatch(RegisteredMemoryRegion<T>, MemRegionId, AsBase, MemoryRegionRDMA<T>, RTMemoryRegionRDMA<T>, LamellarEnv)]
73#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
74#[serde(bound = "T: Dist + serde::Serialize + serde::de::DeserializeOwned")]
75pub enum LamellarMemoryRegion<T: Dist> {
76    ///
77    Shared(SharedMemoryRegion<T>),
78    ///
79    Local(OneSidedMemoryRegion<T>),
80    // Unsafe(UnsafeArray<T>),
81}
82
83// This could be useful for if we want to transfer the actual data instead of the pointer
84// impl<T: Dist + serde::Serialize> LamellarMemoryRegion<T> {
85//     //#[tracing::instrument(skip_all)]
86//     pub(crate) fn serialize_local_data<S>(
87//         mr: &LamellarMemoryRegion<T>,
88//         s: S,
89//     ) -> Result<S::Ok, S::Error>
90//     where
91//         S: serde::Serializer,
92//     {
93//         match mr {
94//             LamellarMemoryRegion::Shared(mr) => mr.serialize_local_data(s),
95//             LamellarMemoryRegion::Local(mr) => mr.serialize_local_data(s),
96//             // LamellarMemoryRegion::Unsafe(mr) => mr.serialize_local_data(s),
97//         }
98//     }
99// }
100
101impl<T: Dist> crate::active_messaging::DarcSerde for LamellarMemoryRegion<T> {
102    //#[tracing::instrument(skip_all)]
103    fn ser(&self, num_pes: usize, darcs: &mut Vec<RemotePtr>) {
104        // println!("in shared ser");
105        match self {
106            LamellarMemoryRegion::Shared(mr) => mr.ser(num_pes, darcs),
107            LamellarMemoryRegion::Local(mr) => mr.ser(num_pes, darcs),
108            // LamellarMemoryRegion::Unsafe(mr) => mr.ser(num_pes,darcs),
109        }
110    }
111    //#[tracing::instrument(skip_all)]
112    fn des(&self, cur_pe: Result<usize, crate::IdError>) {
113        // println!("in shared des");
114        match self {
115            LamellarMemoryRegion::Shared(mr) => mr.des(cur_pe),
116            LamellarMemoryRegion::Local(mr) => mr.des(cur_pe),
117            // LamellarMemoryRegion::Unsafe(mr) => mr.des(cur_pe),
118        }
119        // self.mr.print();
120    }
121}
122
123impl<T: Dist> LamellarMemoryRegion<T> {
124    //#[tracing::instrument(skip_all)]
125    /// If the memory region contains local data, return it as a mutable slice
126    /// else return an error
127    pub unsafe fn as_mut_slice(&self) -> MemResult<&mut [T]> {
128        match self {
129            LamellarMemoryRegion::Shared(memregion) => memregion.as_mut_slice(),
130            LamellarMemoryRegion::Local(memregion) => memregion.as_mut_slice(),
131            // LamellarMemoryRegion::Unsafe(memregion) => memregion.as_mut_slice(),
132        }
133    }
134
135    //#[tracing::instrument(skip_all)]
136    /// if the memory region contains local data, return it as a slice
137    /// else return an error
138    pub unsafe fn as_slice(&self) -> MemResult<&[T]> {
139        match self {
140            LamellarMemoryRegion::Shared(memregion) => memregion.as_slice(),
141            LamellarMemoryRegion::Local(memregion) => memregion.as_slice(),
142            // LamellarMemoryRegion::Unsafe(memregion) => memregion.as_slice(),
143        }
144    }
145
146    // //#[tracing::instrument(skip_all)]
147    // pub fn sub_region<R: std::ops::RangeBounds<usize>>(&self, range: R) -> LamellarMemoryRegion<T> {
148    //     match self {
149    //         LamellarMemoryRegion::Shared(memregion) => memregion.sub_region(range).into(),
150    //         LamellarMemoryRegion::Local(memregion) => memregion.sub_region(range).into(),
151    //         // LamellarMemoryRegion::Unsafe(memregion) => memregion.sub_region(range).into(),
152    //     }
153    // }
154}
155impl<T: Dist> SubRegion<T> for LamellarMemoryRegion<T> {
156    type Region = LamellarMemoryRegion<T>;
157    fn sub_region<R: std::ops::RangeBounds<usize>>(&self, range: R) -> Self::Region {
158        match self {
159            LamellarMemoryRegion::Shared(memregion) => memregion.sub_region(range).into(),
160            LamellarMemoryRegion::Local(memregion) => memregion.sub_region(range).into(),
161        }
162    }
163}
164
165impl<T: Dist> From<LamellarArrayRdmaOutput<T>> for LamellarMemoryRegion<T> {
166    //#[tracing::instrument(skip_all)]
167    fn from(output: LamellarArrayRdmaOutput<T>) -> Self {
168        match output {
169            LamellarArrayRdmaOutput::LamellarMemRegion(mr) => mr,
170            LamellarArrayRdmaOutput::SharedMemRegion(mr) => mr.into(),
171            LamellarArrayRdmaOutput::LocalMemRegion(mr) => mr.into(),
172        }
173    }
174}
175
176impl<T: Dist> From<LamellarArrayRdmaInput<T>> for LamellarMemoryRegion<T> {
177    //#[tracing::instrument(skip_all)]
178    fn from(input: LamellarArrayRdmaInput<T>) -> Self {
179        match input {
180            LamellarArrayRdmaInput::LamellarMemRegion(mr) => mr,
181            LamellarArrayRdmaInput::SharedMemRegion(mr) => mr.into(),
182            LamellarArrayRdmaInput::LocalMemRegion(mr) => mr.into(),
183        }
184    }
185}
186
187impl<T: Dist> From<&LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
188    //#[tracing::instrument(skip_all)]
189    fn from(mr: &LamellarMemoryRegion<T>) -> Self {
190        LamellarArrayRdmaInput::LamellarMemRegion(mr.clone())
191    }
192}
193
194impl<T: Dist> TeamFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
195    //#[tracing::instrument(skip_all)]
196    fn team_from(mr: &LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
197        LamellarArrayRdmaInput::LamellarMemRegion(mr.clone())
198    }
199}
200
201impl<T: Dist> TeamFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
202    //#[tracing::instrument(skip_all)]
203    fn team_from(mr: LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
204        LamellarArrayRdmaInput::LamellarMemRegion(mr)
205    }
206}
207
208impl<T: Dist> TeamTryFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
209    //#[tracing::instrument(skip_all)]
210    fn team_try_from(
211        mr: &LamellarMemoryRegion<T>,
212        _team: &Arc<LamellarTeam>,
213    ) -> Result<Self, anyhow::Error> {
214        Ok(LamellarArrayRdmaInput::LamellarMemRegion(mr.clone()))
215    }
216}
217
218impl<T: Dist> TeamTryFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
219    //#[tracing::instrument(skip_all)]
220    fn team_try_from(
221        mr: LamellarMemoryRegion<T>,
222        _team: &Arc<LamellarTeam>,
223    ) -> Result<Self, anyhow::Error> {
224        Ok(LamellarArrayRdmaInput::LamellarMemRegion(mr))
225    }
226}
227
228impl<T: Dist> From<&LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
229    //#[tracing::instrument(skip_all)]
230    fn from(mr: &LamellarMemoryRegion<T>) -> Self {
231        LamellarArrayRdmaOutput::LamellarMemRegion(mr.clone())
232    }
233}
234
235impl<T: Dist> TeamFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
236    //#[tracing::instrument(skip_all)]
237    fn team_from(mr: &LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
238        LamellarArrayRdmaOutput::LamellarMemRegion(mr.clone())
239    }
240}
241
242impl<T: Dist> TeamFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
243    //#[tracing::instrument(skip_all)]
244    fn team_from(mr: LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
245        LamellarArrayRdmaOutput::LamellarMemRegion(mr)
246    }
247}
248
249impl<T: Dist> TeamTryFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
250    //#[tracing::instrument(skip_all)]
251    fn team_try_from(
252        mr: &LamellarMemoryRegion<T>,
253        _team: &Arc<LamellarTeam>,
254    ) -> Result<Self, anyhow::Error> {
255        Ok(LamellarArrayRdmaOutput::LamellarMemRegion(mr.clone()))
256    }
257}
258
259impl<T: Dist> TeamTryFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
260    //#[tracing::instrument(skip_all)]
261    fn team_try_from(
262        mr: LamellarMemoryRegion<T>,
263        _team: &Arc<LamellarTeam>,
264    ) -> Result<Self, anyhow::Error> {
265        Ok(LamellarArrayRdmaOutput::LamellarMemRegion(mr))
266    }
267}
268/// An  abstraction for a memory region that has been registered with the underlying lamellae (network provider)
269/// allowing for RDMA operations.
270///
271/// Memory Regions are low-level unsafe abstraction not really intended for use in higher-level applications
272///
273///  
274/// Unless you are very confident in low level distributed memory access it is highly recommended you utilize the
275/// [LamellarArray][crate::array::LamellarArray] interface to construct and interact with distributed memory.
276#[enum_dispatch]
277pub trait RegisteredMemoryRegion<T: Dist> {
278    #[doc(alias("One-sided", "onesided"))]
279    /// The length (in number of elements of `T`) of the local segment of the memory region (i.e. not the global length of the memory region)  
280    ///
281    /// # One-sided Operation
282    /// the result is returned only on the calling PE
283    ///
284    /// # Examples
285    ///```
286    /// use lamellar::memregion::prelude::*;
287    ///
288    /// let world = LamellarWorldBuilder::new().build();
289    ///
290    /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
291    /// assert_eq!(mem_region.len(),1000);
292    ///```
293    fn len(&self) -> usize;
294
295    //TODO: move this function to a private trait or private method
296    #[doc(hidden)]
297    fn addr(&self) -> MemResult<usize>;
298
299    #[doc(alias("One-sided", "onesided"))]
300    /// Return a slice of the local (to the calling PE) data of the memory region
301    ///
302    /// Returns an error if the PE does not contain any local data associated with this memory region
303    ///
304    /// # Safety
305    /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
306    ///
307    /// # One-sided Operation
308    /// the result is returned only on the calling PE
309    ///
310    /// # Examples
311    ///```
312    /// use lamellar::memregion::prelude::*;
313    ///
314    /// let world = LamellarWorldBuilder::new().build();
315    ///
316    /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
317    /// let slice = unsafe{mem_region.as_slice().expect("PE is part of the world team")};
318    ///```
319    unsafe fn as_slice(&self) -> MemResult<&[T]>;
320
321    #[doc(alias("One-sided", "onesided"))]
322    /// Return a reference to the local (to the calling PE) element located by the provided index
323    ///
324    /// Returns an error if the index is out of bounds or the PE does not contain any local data associated with this memory region
325    ///
326    /// # Safety
327    /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
328    ///
329    /// # One-sided Operation
330    /// the result is returned only on the calling PE
331    ///
332    /// # Examples
333    ///```
334    /// use lamellar::memregion::prelude::*;
335    ///
336    /// let world = LamellarWorldBuilder::new().build();
337    ///
338    /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
339    /// let val = unsafe{mem_region.at(999).expect("PE is part of the world team")};
340    ///```
341    unsafe fn at(&self, index: usize) -> MemResult<&T>;
342
343    #[doc(alias("One-sided", "onesided"))]
344    /// Return a mutable slice of the local (to the calling PE) data of the memory region
345    ///
346    /// Returns an error if the PE does not contain any local data associated with this memory region
347    ///
348    /// # Safety
349    /// this call is always unsafe as there is no gaurantee that there do not exist other mutable references elsewhere in the distributed system.
350    ///
351    /// # One-sided Operation
352    /// the result is returned only on the calling PE
353    ///
354    /// # Examples
355    ///```
356    /// use lamellar::memregion::prelude::*;
357    ///
358    /// let world = LamellarWorldBuilder::new().build();
359    ///
360    /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
361    /// let slice =unsafe { mem_region.as_mut_slice().expect("PE is part of the world team")};
362    ///```
363    unsafe fn as_mut_slice(&self) -> MemResult<&mut [T]>;
364
365    #[doc(alias("One-sided", "onesided"))]
366    /// Return a ptr to the local (to the calling PE) data of the memory region
367    ///
368    /// Returns an error if the PE does not contain any local data associated with this memory region
369    ///
370    /// # Safety
371    /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
372    ///
373    /// # One-sided Operation
374    /// the result is returned only on the calling PE
375    ///
376    /// # Examples
377    ///```
378    /// use lamellar::memregion::prelude::*;
379    ///
380    /// let world = LamellarWorldBuilder::new().build();
381    ///
382    /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
383    /// let ptr = unsafe { mem_region.as_ptr().expect("PE is part of the world team")};
384    ///```
385    unsafe fn as_ptr(&self) -> MemResult<*const T>;
386
387    #[doc(alias("One-sided", "onesided"))]
388    /// Return a mutable ptr to the local (to the calling PE) data of the memory region
389    ///
390    /// Returns an error if the PE does not contain any local data associated with this memory region
391    ///
392    /// # Safety
393    /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
394    ///
395    /// # One-sided Operation
396    /// the result is returned only on the calling PE
397    ///
398    /// # Examples
399    ///```
400    /// use lamellar::memregion::prelude::*;
401    ///
402    /// let world = LamellarWorldBuilder::new().build();
403    ///
404    /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
405    /// let ptr = unsafe { mem_region.as_mut_ptr().expect("PE is part of the world team")};
406    ///```
407    unsafe fn as_mut_ptr(&self) -> MemResult<*mut T>;
408}
409
410#[enum_dispatch]
411pub(crate) trait MemRegionId {
412    fn id(&self) -> usize;
413}
414
415// RegisteredMemoryRegion<T>, MemRegionId, AsBase, SubRegion<T>, MemoryRegionRDMA<T>, RTMemoryRegionRDMA<T>
416// we seperate SubRegion and AsBase out as their own traits
417// because we want MemRegion to impl RegisteredMemoryRegion (so that it can be used in Shared + Local)
418// but MemRegion should not return LamellarMemoryRegions directly (as both SubRegion and AsBase require)
419// we will implement seperate functions for MemoryRegion itself.
420//#[doc(hidden)]
421
422/// Trait for creating subregions of a memory region
423#[enum_dispatch]
424pub trait SubRegion<T: Dist> {
425    #[doc(hidden)]
426    type Region: RegisteredMemoryRegion<T> + MemoryRegionRDMA<T>;
427    #[doc(alias("One-sided", "onesided"))]
428    /// Create a sub region of this RegisteredMemoryRegion using the provided range
429    ///
430    /// # One-sided Operation
431    /// the result is returned only on the calling PE
432    ///
433    /// # Panics
434    /// panics if the end range is larger than the length of the memory region
435    /// # Examples
436    ///```
437    /// use lamellar::memregion::prelude::*;
438    ///
439    /// let world = LamellarWorldBuilder::new().build();
440    /// let my_pe = world.my_pe();
441    /// let num_pes = world.num_pes();
442    ///
443    /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(100).block();
444    ///
445    /// let sub_region = mem_region.sub_region(30..70);
446    ///```
447    fn sub_region<R: std::ops::RangeBounds<usize>>(&self, range: R) -> Self::Region;
448}
449
450#[enum_dispatch]
451pub(crate) trait AsBase {
452    unsafe fn to_base<B: Dist>(self) -> LamellarMemoryRegion<B>;
453}
454
455/// The Inteface for exposing RDMA operations on a memory region. These provide the actual mechanism for performing a transfer.
456#[enum_dispatch]
457pub trait MemoryRegionRDMA<T: Dist> {
458    #[doc(alias("One-sided", "onesided"))]
459    /// "Puts" (copies) data from a local memory location into a remote memory location on the specified PE
460    ///
461    /// The data buffer may not be safe to upon return from this call, currently the user is responsible for completion detection,
462    /// or you may use the similar blocking_put call (with a potential performance penalty);
463    ///
464    /// # Safety
465    /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
466    /// Additionally, when this call returns the underlying fabric provider may or may not have already copied the data buffer
467    ///
468    /// # One-sided Operation
469    /// the calling PE initaites the remote transfer
470    ///
471    /// # Examples
472    ///```
473    /// use lamellar::memregion::prelude::*;
474    ///
475    /// let world = LamellarWorldBuilder::new().build();
476    /// let my_pe = world.my_pe();
477    /// let num_pes = world.num_pes();
478    ///
479    /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
480    /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
481    /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
482    /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
483    ///
484    /// for pe in 0..num_pes{
485    ///    unsafe{dst_mem_region.put(pe,my_pe*src_mem_region.len(),&src_mem_region)};
486    /// }
487    /// unsafe {
488    ///     let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
489    ///     for (i,elem) in dst_slice.iter().enumerate(){
490    ///         let pe = i / &src_mem_region.len();
491    ///         while *elem == num_pes{
492    ///             std::thread::yield_now();
493    ///         }
494    ///         assert_eq!(pe,*elem);
495    ///     }      
496    /// }
497    ///```
498    unsafe fn put<U: Into<LamellarMemoryRegion<T>>>(&self, pe: usize, index: usize, data: U);
499
500    #[doc(alias("One-sided", "onesided"))]
501    /// Blocking "Puts" (copies) data from a local memory location into a remote memory location on the specified PE.
502    ///
503    /// This function blocks until the data in the data buffer has been transfered out of this PE, this does not imply that it has arrived at the remote destination though
504    ///
505    /// # Safety
506    /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
507    ///
508    /// # One-sided Operation
509    /// the calling PE initaites the remote transfer
510    ///
511    /// # Examples
512    ///```
513    /// use lamellar::memregion::prelude::*;
514    ///
515    /// let world = LamellarWorldBuilder::new().build();
516    /// let my_pe = world.my_pe();
517    /// let num_pes = world.num_pes();
518    ///
519    /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
520    /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
521    /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
522    /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
523    ///
524    /// for pe in 0..num_pes{
525    ///    unsafe{dst_mem_region.blocking_put(pe,my_pe*src_mem_region.len(),&src_mem_region)};
526    /// }
527    /// unsafe {
528    ///     let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
529    ///     for (i,elem) in dst_slice.iter().enumerate(){
530    ///         let pe = i / &src_mem_region.len();
531    ///         while *elem == num_pes{
532    ///             std::thread::yield_now();
533    ///         }
534    ///         assert_eq!(pe,*elem);
535    ///     }      
536    /// }
537    ///```
538    unsafe fn blocking_put<U: Into<LamellarMemoryRegion<T>>>(
539        &self,
540        pe: usize,
541        index: usize,
542        data: U,
543    );
544
545    /// "Puts" (copies) data from a local memory location into a remote memory location on all PEs containing the memory region
546    ///
547    /// This is similar to broadcast
548    ///
549    /// The data buffer may not be safe to upon return from this call, currently the user is responsible for completion detection
550    ///
551    /// # Safety
552    /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
553    /// Additionally, when this call returns the underlying fabric provider may or may not have already copied the data buffer
554    /// # Examples
555    ///```
556    /// use lamellar::memregion::prelude::*;
557    ///
558    /// let world = LamellarWorldBuilder::new().build();
559    /// let my_pe = world.my_pe();
560    /// let num_pes = world.num_pes();
561    ///
562    /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
563    /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
564    /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
565    /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
566    ///
567    /// unsafe{dst_mem_region.put_all(my_pe*src_mem_region.len(),&src_mem_region)};
568    ///
569    /// unsafe {
570    ///     let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
571    ///     for (i,elem) in dst_slice.iter().enumerate(){
572    ///         let pe = i / &src_mem_region.len();
573    ///         while *elem == num_pes{
574    ///             std::thread::yield_now();
575    ///         }
576    ///         assert_eq!(pe,*elem);
577    ///     }      
578    /// }
579    ///```
580    unsafe fn put_all<U: Into<LamellarMemoryRegion<T>>>(&self, index: usize, data: U);
581
582    #[doc(alias("One-sided", "onesided"))]
583    /// "Gets" (copies) data from remote memory location on the specified PE into the provided data buffer.
584    /// After calling this function, the data may or may not have actually arrived into the data buffer.
585    /// The user is responsible for transmission termination detection
586    ///
587    /// # Safety
588    /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
589    /// Additionally, when this call returns the underlying fabric provider may or may not have already copied data into the data buffer.
590    ///
591    /// # One-sided Operation
592    /// the calling PE initaites the remote transfer
593    ///
594    /// # Examples
595    ///```
596    /// use lamellar::memregion::prelude::*;
597    ///
598    /// let world = LamellarWorldBuilder::new().build();
599    /// let my_pe = world.my_pe();
600    /// let num_pes = world.num_pes();
601    ///
602    /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
603    /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
604    ///
605    /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
606    /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
607    ///
608    /// for pe in 0..num_pes{
609    ///     let start_i = pe*src_mem_region.len();
610    ///     let end_i = start_i+src_mem_region.len();
611    ///     unsafe{src_mem_region.get_unchecked(pe,0,dst_mem_region.sub_region(start_i..end_i))};
612    /// }
613    ///
614    /// unsafe {
615    ///     let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
616    ///     for (i,elem) in dst_slice.iter().enumerate(){
617    ///         let pe = i / &src_mem_region.len();
618    ///         while *elem == num_pes{
619    ///             std::thread::yield_now();
620    ///         }
621    ///         assert_eq!(pe,*elem);
622    ///     }      
623    /// }
624    ///```
625    unsafe fn get_unchecked<U: Into<LamellarMemoryRegion<T>>>(
626        &self,
627        pe: usize,
628        index: usize,
629        data: U,
630    );
631
632    #[doc(alias("One-sided", "onesided"))]
633    /// Blocking "Gets" (copies) data from remote memory location on the specified PE into the provided data buffer.
634    /// After calling this function, the data is guaranteed to be placed in the data buffer
635    ///
636    /// # Safety
637    /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
638    ///
639    /// # One-sided Operation
640    /// the calling PE initaites the remote transfer
641    ///
642    /// # Examples
643    ///```
644    /// use lamellar::memregion::prelude::*;
645    ///
646    /// let world = LamellarWorldBuilder::new().build();
647    /// let my_pe = world.my_pe();
648    /// let num_pes = world.num_pes();
649    ///
650    /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
651    /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
652    ///
653    /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
654    /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
655    ///
656    /// for pe in 0..num_pes{
657    ///     let start_i = pe*src_mem_region.len();
658    ///     let end_i = start_i+src_mem_region.len();
659    ///     unsafe{src_mem_region.blocking_get(pe,0,dst_mem_region.sub_region(start_i..end_i))};
660    /// }
661    ///
662    /// unsafe {
663    ///     let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
664    ///     for (i,elem) in dst_slice.iter().enumerate(){
665    ///         let pe = i / &src_mem_region.len();
666    ///         assert_eq!(pe,*elem);
667    ///     }      
668    /// }
669    ///```
670    unsafe fn blocking_get<U: Into<LamellarMemoryRegion<T>>>(
671        &self,
672        pe: usize,
673        index: usize,
674        data: U,
675    );
676}
677
678#[enum_dispatch]
679pub(crate) trait RTMemoryRegionRDMA<T: Dist> {
680    unsafe fn put_slice(&self, pe: usize, index: usize, data: &[T]);
681    unsafe fn blocking_get_slice(&self, pe: usize, index: usize, data: &mut [T]);
682}
683
684impl<T: Dist> Hash for LamellarMemoryRegion<T> {
685    //#[tracing::instrument(skip_all)]
686    fn hash<H: Hasher>(&self, state: &mut H) {
687        self.id().hash(state);
688    }
689}
690
691impl<T: Dist> PartialEq for LamellarMemoryRegion<T> {
692    //#[tracing::instrument(skip_all)]
693    fn eq(&self, other: &LamellarMemoryRegion<T>) -> bool {
694        self.id() == other.id()
695    }
696}
697
698impl<T: Dist> Eq for LamellarMemoryRegion<T> {}
699
700impl<T: Dist> LamellarWrite for LamellarMemoryRegion<T> {}
701impl<T: Dist> LamellarWrite for &LamellarMemoryRegion<T> {}
702impl<T: Dist> LamellarRead for LamellarMemoryRegion<T> {}
703impl<T: Dist> LamellarRead for &LamellarMemoryRegion<T> {}
704
705#[derive(Copy, Clone)]
706pub(crate) enum Mode {
707    Local,
708    Remote,
709    Shared,
710}
711
712// this is not intended to be accessed directly by a user
713// it will be wrapped in either a shared region or local region
714// in shared regions its wrapped in a darc which allows us to send
715// to different nodes, in local its wrapped in Arc (we dont currently support sending to other nodes)
716// for local we would probably need to develop something like a one-sided initiated darc...
717pub(crate) struct MemoryRegion<T: Dist> {
718    addr: usize,
719    pe: usize,
720    size: usize,
721    num_bytes: usize,
722    backend: Backend,
723    rdma: Arc<dyn LamellaeRDMA>,
724    mode: Mode,
725    phantom: PhantomData<T>,
726}
727
728impl<T: Dist> MemoryRegion<T> {
729    //#[tracing::instrument(skip_all)]
730    pub(crate) fn new(
731        size: usize, //number of elements of type T
732        lamellae: Arc<Lamellae>,
733        alloc: AllocationType,
734    ) -> MemoryRegion<T> {
735        if let Ok(memreg) = MemoryRegion::try_new(size, lamellae, alloc) {
736            memreg
737        } else {
738            unsafe { std::ptr::null_mut::<i32>().write(1) };
739            panic!("out of memory")
740        }
741    }
742    //#[tracing::instrument(skip_all)]
743    pub(crate) fn try_new(
744        size: usize, //number of elements of type T
745        lamellae: Arc<Lamellae>,
746        alloc: AllocationType,
747    ) -> Result<MemoryRegion<T>, anyhow::Error> {
748        // println!(
749        //     "creating new lamellar memory region size: {:?} align: {:?}",
750        //     size * std::mem::size_of::<T>(),
751        //     std::mem::align_of::<T>()
752        // );
753        let mut mode = Mode::Shared;
754        let addr = if size > 0 {
755            if let AllocationType::Local = alloc {
756                mode = Mode::Local;
757                lamellae.rt_alloc(size * std::mem::size_of::<T>(), std::mem::align_of::<T>())?
758            } else {
759                lamellae.alloc(
760                    size * std::mem::size_of::<T>(),
761                    alloc,
762                    std::mem::align_of::<T>(),
763                )? //did we call team barrer before this?
764            }
765        } else {
766            println!(
767                "cant have zero sized memregion {:?}",
768                std::backtrace::Backtrace::capture()
769            );
770            panic!("cant have zero sized memregion");
771            // return Err(anyhow::anyhow!("cant have negative sized memregion"));
772        };
773        let temp = MemoryRegion {
774            addr: addr,
775            pe: lamellae.my_pe(),
776            size: size,
777            num_bytes: size * std::mem::size_of::<T>(),
778            backend: lamellae.backend(),
779            rdma: lamellae,
780            mode: mode,
781            phantom: PhantomData,
782        };
783        // println!(
784        //     "new memregion {:x} {:x}",
785        //     temp.addr,
786        //     size * std::mem::size_of::<T>()
787        // );
788        Ok(temp)
789    }
790    //#[tracing::instrument(skip_all)]
791    pub(crate) fn from_remote_addr(
792        addr: usize,
793        pe: usize,
794        size: usize,
795        lamellae: Arc<Lamellae>,
796    ) -> Result<MemoryRegion<T>, anyhow::Error> {
797        Ok(MemoryRegion {
798            addr: addr,
799            pe: pe,
800            size: size,
801            num_bytes: size * std::mem::size_of::<T>(),
802            backend: lamellae.backend(),
803            rdma: lamellae,
804            mode: Mode::Remote,
805            phantom: PhantomData,
806        })
807    }
808
809    #[allow(dead_code)]
810    //#[tracing::instrument(skip_all)]
811    pub(crate) unsafe fn to_base<B: Dist>(mut self) -> MemoryRegion<B> {
812        //this is allowed as we consume the old object..
813        assert_eq!(
814            self.num_bytes % std::mem::size_of::<B>(),
815            0,
816            "Error converting memregion to new base, does not align"
817        );
818        // MemoryRegion {
819        //     addr: self.addr, //TODO: out of memory...
820        //     pe: self.pe,
821        //     size: self.num_bytes / std::mem::size_of::<B>(),
822        //     num_bytes: self.num_bytes,
823        //     backend: self.backend,
824        //     rdma: self.rdma.clone(),
825        //     mode: self.mode,
826        //     phantom: PhantomData,
827        // }
828        self.size = self.num_bytes / std::mem::size_of::<B>();
829        std::mem::transmute(self) //we do this because other wise self gets dropped and frees the underlying data (we could also set addr to 0 in self)
830    }
831
832    // }
833
834    // impl<T: AmDist+ 'static> MemoryRegionRDMA<T> for MemoryRegion<T> {
835    /// copy data from local memory location into a remote memory location
836    ///
837    /// # Arguments
838    ///
839    /// * `pe` - id of remote PE to grab data from
840    /// * `index` - offset into the remote memory window
841    /// * `data` - address (which is "registered" with network device) of local input buffer that will be put into the remote memory
842    /// the data buffer may not be safe to upon return from this call, currently the user is responsible for completion detection,
843    /// or you may use the similar iput call (with a potential performance penalty);
844    //#[tracing::instrument(skip_all)]
845    pub(crate) unsafe fn put<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
846        &self,
847        pe: usize,
848        index: usize,
849        data: U,
850    ) {
851        //todo make return a result?
852        let data = data.into();
853        if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
854            let num_bytes = data.len() * std::mem::size_of::<R>();
855            if let Ok(ptr) = data.as_ptr() {
856                let bytes = std::slice::from_raw_parts(ptr as *const u8, num_bytes);
857                self.rdma
858                    .put(pe, bytes, self.addr + index * std::mem::size_of::<R>())
859            } else {
860                panic!("ERROR: put data src is not local");
861            }
862        } else {
863            println!(
864                "mem region bytes: {:?} sizeof elem {:?} len {:?}",
865                self.num_bytes,
866                std::mem::size_of::<T>(),
867                self.size
868            );
869            println!(
870                "data bytes: {:?} sizeof elem {:?} len {:?} index: {:?}",
871                data.len() * std::mem::size_of::<R>(),
872                std::mem::size_of::<R>(),
873                data.len(),
874                index
875            );
876            panic!("index out of bounds");
877        }
878    }
879
880    /// copy data from local memory location into a remote memory localtion
881    ///
882    /// # Arguments
883    ///
884    /// * `pe` - id of remote PE to grab data from
885    /// * `index` - offset into the remote memory window
886    /// * `data` - address (which is "registered" with network device) of local input buffer that will be put into the remote memory
887    /// the data buffer is free to be reused upon return of this function.
888    //#[tracing::instrument(skip_all)]
889    pub(crate) unsafe fn blocking_put<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
890        &self,
891        pe: usize,
892        index: usize,
893        data: U,
894    ) {
895        //todo make return a result?
896        let data = data.into();
897        if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
898            let num_bytes = data.len() * std::mem::size_of::<R>();
899            if let Ok(ptr) = data.as_ptr() {
900                let bytes = std::slice::from_raw_parts(ptr as *const u8, num_bytes);
901                self.rdma
902                    .iput(pe, bytes, self.addr + index * std::mem::size_of::<R>())
903            } else {
904                panic!("ERROR: put data src is not local");
905            }
906        } else {
907            println!("{:?} {:?} {:?}", self.size, index, data.len());
908            panic!("index out of bounds");
909        }
910    }
911
912    //#[tracing::instrument(skip_all)]
913    pub(crate) unsafe fn put_all<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
914        &self,
915        index: usize,
916        data: U,
917    ) {
918        let data = data.into();
919        if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
920            let num_bytes = data.len() * std::mem::size_of::<R>();
921            if let Ok(ptr) = data.as_ptr() {
922                let bytes = std::slice::from_raw_parts(ptr as *const u8, num_bytes);
923                self.rdma
924                    .put_all(bytes, self.addr + index * std::mem::size_of::<R>());
925            } else {
926                panic!("ERROR: put data src is not local");
927            }
928        } else {
929            panic!("index out of bounds");
930        }
931    }
932
933    //TODO: once we have a reliable asynchronos get wait mechanism, we return a request handle,
934    //data probably needs to be referenced count or lifespan controlled so we know it exists when the get trys to complete
935    //in the handle drop method we will wait until the request completes before dropping...  ensuring the data has a place to go
936    /// copy data from remote memory location into provided data buffer
937    ///
938    /// # Arguments
939    ///
940    /// * `pe` - id of remote PE to grab data from
941    /// * `index` - offset into the remote memory window
942    /// * `data` - address (which is "registered" with network device) of destination buffer to store result of the get
943    //#[tracing::instrument(skip_all)]
944    pub(crate) unsafe fn get_unchecked<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
945        &self,
946        pe: usize,
947        index: usize,
948        data: U,
949    ) {
950        let data = data.into();
951        if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
952            let num_bytes = data.len() * std::mem::size_of::<R>();
953            if let Ok(ptr) = data.as_mut_ptr() {
954                let bytes = std::slice::from_raw_parts_mut(ptr as *mut u8, num_bytes);
955                // println!("getting {:?} {:?} {:?} {:?} {:?} {:?} {:?}",pe,index,std::mem::size_of::<R>(),data.len(), num_bytes,self.size, self.num_bytes);
956                self.rdma
957                    .get(pe, self.addr + index * std::mem::size_of::<R>(), bytes);
958            //(remote pe, src, dst)
959            // println!("getting {:?} {:?} [{:?}] {:?} {:?} {:?}",pe,self.addr + index * std::mem::size_of::<T>(),index,data.addr(),data.len(),num_bytes);
960            } else {
961                panic!("ERROR: get data dst is not local");
962            }
963        } else {
964            println!("{:?} {:?} {:?}", self.size, index, data.len(),);
965            panic!("index out of bounds");
966        }
967    }
968
969    /// copy data from remote memory location into provided data buffer
970    ///
971    /// # Arguments
972    ///
973    /// * `pe` - id of remote PE to grab data from
974    /// * `index` - offset into the remote memory window
975    /// * `data` - address (which is "registered" with network device) of destination buffer to store result of the get
976    ///    data will be present within the buffer once this returns.
977    //#[tracing::instrument(skip_all)]
978    pub(crate) unsafe fn blocking_get<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
979        &self,
980        pe: usize,
981        index: usize,
982        data: U,
983    ) {
984        let data = data.into();
985        if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
986            let num_bytes = data.len() * std::mem::size_of::<R>();
987            if let Ok(ptr) = data.as_mut_ptr() {
988                let bytes = std::slice::from_raw_parts_mut(ptr as *mut u8, num_bytes);
989                // println!(
990                //     "getting {:?} {:?} {:?} {:?} {:?} {:?} {:?}",
991                //     pe,
992                //     index,
993                //     std::mem::size_of::<R>(),
994                //     data.len(),
995                //     num_bytes,
996                //     self.size,
997                //     self.num_bytes
998                // );
999                self.rdma
1000                    .iget(pe, self.addr + index * std::mem::size_of::<R>(), bytes);
1001            //(remote pe, src, dst)
1002            // println!("getting {:?} {:?} [{:?}] {:?} {:?} {:?}",pe,self.addr + index * std::mem::size_of::<T>(),index,data.addr(),data.len(),num_bytes);
1003            } else {
1004                panic!("ERROR: get data dst is not local");
1005            }
1006        } else {
1007            println!("{:?} {:?} {:?}", self.size, index, data.len(),);
1008            panic!("index out of bounds");
1009        }
1010    }
1011
1012    //we must ensure the the slice will live long enough and that it already exsists in registered memory
1013    //#[tracing::instrument(skip_all)]
1014    pub(crate) unsafe fn put_slice<R: Dist>(&self, pe: usize, index: usize, data: &[R]) {
1015        //todo make return a result?
1016        if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
1017            let num_bytes = data.len() * std::mem::size_of::<R>();
1018            let bytes = std::slice::from_raw_parts(data.as_ptr() as *const u8, num_bytes);
1019            // println!(
1020            //     "mem region len: {:?} index: {:?} data len{:?} num_bytes {:?}  from {:?} to {:x} ({:x} [{:?}])",
1021            //     self.size,
1022            //     index,
1023            //     data.len(),
1024            //     num_bytes,
1025            //     data.as_ptr(),
1026            //     self.addr,
1027            //     self.addr + index * std::mem::size_of::<T>(),
1028            //     pe,
1029            // );
1030            self.rdma
1031                .put(pe, bytes, self.addr + index * std::mem::size_of::<R>())
1032        } else {
1033            println!(
1034                "mem region len: {:?} index: {:?} data len{:?}",
1035                self.size,
1036                index,
1037                data.len()
1038            );
1039            panic!("index out of bounds");
1040        }
1041    }
1042    /// copy data from remote memory location into provided data buffer
1043    ///
1044    /// # Arguments
1045    ///
1046    /// * `pe` - id of remote PE to grab data from
1047    /// * `index` - offset into the remote memory window
1048    /// * `data` - address (which is "registered" with network device) of destination buffer to store result of the get
1049    ///    data will be present within the buffer once this returns.
1050    //#[tracing::instrument(skip_all)]
1051    pub(crate) unsafe fn blocking_get_slice<R: Dist>(
1052        &self,
1053        pe: usize,
1054        index: usize,
1055        data: &mut [R],
1056    ) {
1057        // let data = data.into();
1058        if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
1059            let num_bytes = data.len() * std::mem::size_of::<R>();
1060            let bytes = std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, num_bytes);
1061            // println!("getting {:?} {:?} {:?} {:?} {:?} {:?} {:?}",pe,index,std::mem::size_of::<R>(),data.len(), num_bytes,self.size, self.num_bytes);
1062
1063            self.rdma
1064                .iget(pe, self.addr + index * std::mem::size_of::<R>(), bytes);
1065            //(remote pe, src, dst)
1066            // println!("getting {:?} {:?} [{:?}] {:?} {:?} {:?}",pe,self.addr + index * std::mem::size_of::<T>(),index,data.addr(),data.len(),num_bytes);
1067        } else {
1068            println!("{:?} {:?} {:?}", self.size, index, data.len(),);
1069            panic!("index out of bounds");
1070        }
1071    }
1072
1073    #[allow(dead_code)]
1074    //#[tracing::instrument(skip_all)]
1075    pub(crate) unsafe fn fill_from_remote_addr<R: Dist>(
1076        &self,
1077        my_index: usize,
1078        pe: usize,
1079        addr: usize,
1080        len: usize,
1081    ) {
1082        if (my_index + len) * std::mem::size_of::<R>() <= self.num_bytes {
1083            let num_bytes = len * std::mem::size_of::<R>();
1084            let my_offset = self.addr + my_index * std::mem::size_of::<R>();
1085            let bytes = std::slice::from_raw_parts_mut(my_offset as *mut u8, num_bytes);
1086            let local_addr = self.rdma.local_addr(pe, addr);
1087            self.rdma.iget(pe, local_addr, bytes);
1088        } else {
1089            println!(
1090                "mem region len: {:?} index: {:?} data len{:?}",
1091                self.size, my_index, len
1092            );
1093            panic!("index out of bounds");
1094        }
1095    }
1096
1097    #[allow(dead_code)]
1098    //#[tracing::instrument(skip_all)]
1099    pub(crate) fn len(&self) -> usize {
1100        self.size
1101    }
1102
1103    //#[tracing::instrument(skip_all)]
1104    pub(crate) fn addr(&self) -> MemResult<usize> {
1105        Ok(self.addr)
1106    }
1107
1108    //#[tracing::instrument(skip_all)]
1109    pub(crate) fn casted_at<R: Dist>(&self, index: usize) -> MemResult<&R> {
1110        if self.addr != 0 {
1111            let num_bytes = self.size * std::mem::size_of::<T>();
1112            assert_eq!(
1113                num_bytes % std::mem::size_of::<R>(),
1114                0,
1115                "Error converting memregion to new base, does not align"
1116            );
1117            Ok(unsafe {
1118                &std::slice::from_raw_parts(
1119                    self.addr as *const R,
1120                    num_bytes / std::mem::size_of::<R>(),
1121                )[index]
1122            })
1123        } else {
1124            Err(MemNotLocalError {})
1125        }
1126    }
1127
1128    //#[tracing::instrument(skip_all)]
1129    pub(crate) fn as_slice(&self) -> MemResult<&[T]> {
1130        if self.addr != 0 {
1131            Ok(unsafe { std::slice::from_raw_parts(self.addr as *const T, self.size) })
1132        } else {
1133            Ok(&[])
1134        }
1135    }
1136    //#[tracing::instrument(skip_all)]
1137    pub(crate) fn as_casted_slice<R: Dist>(&self) -> MemResult<&[R]> {
1138        if self.addr != 0 {
1139            let num_bytes = self.size * std::mem::size_of::<T>();
1140            assert_eq!(
1141                num_bytes % std::mem::size_of::<R>(),
1142                0,
1143                "Error converting memregion to new base, does not align"
1144            );
1145            Ok(unsafe {
1146                std::slice::from_raw_parts(
1147                    self.addr as *const R,
1148                    num_bytes / std::mem::size_of::<R>(),
1149                )
1150            })
1151        } else {
1152            Ok(&[])
1153        }
1154    }
1155    //#[tracing::instrument(skip_all)]
1156    pub(crate) unsafe fn as_mut_slice(&self) -> MemResult<&mut [T]> {
1157        if self.addr != 0 {
1158            Ok(std::slice::from_raw_parts_mut(
1159                self.addr as *mut T,
1160                self.size,
1161            ))
1162        } else {
1163            Ok(&mut [])
1164        }
1165    }
1166    //#[tracing::instrument(skip_all)]
1167    pub(crate) unsafe fn as_casted_mut_slice<R: Dist>(&self) -> MemResult<&mut [R]> {
1168        if self.addr != 0 {
1169            let num_bytes = self.size * std::mem::size_of::<T>();
1170            assert_eq!(
1171                num_bytes % std::mem::size_of::<R>(),
1172                0,
1173                "Error converting memregion to new base, does not align"
1174            );
1175            Ok(std::slice::from_raw_parts_mut(
1176                self.addr as *mut R,
1177                num_bytes / std::mem::size_of::<R>(),
1178            ))
1179        } else {
1180            Ok(&mut [])
1181        }
1182    }
1183    #[allow(dead_code)]
1184    //#[tracing::instrument(skip_all)]
1185    pub(crate) fn as_ptr(&self) -> MemResult<*const T> {
1186        Ok(self.addr as *const T)
1187    }
1188    #[allow(dead_code)]
1189    //#[tracing::instrument(skip_all)]
1190    pub(crate) fn as_casted_ptr<R: Dist>(&self) -> MemResult<*const R> {
1191        Ok(self.addr as *const R)
1192    }
1193    #[allow(dead_code)]
1194    //#[tracing::instrument(skip_all)]
1195    pub(crate) fn as_mut_ptr(&self) -> MemResult<*mut T> {
1196        Ok(self.addr as *mut T)
1197    }
1198    #[allow(dead_code)]
1199    //#[tracing::instrument(skip_all)]
1200    pub(crate) fn as_casted_mut_ptr<R: Dist>(&self) -> MemResult<*mut R> {
1201        Ok(self.addr as *mut R)
1202    }
1203}
1204
1205impl<T: Dist> MemRegionId for MemoryRegion<T> {
1206    //#[tracing::instrument(skip_all)]
1207    fn id(&self) -> usize {
1208        self.addr //probably should be key
1209    }
1210}
1211
1212/// The interface for allocating shared and onesided memory regions
1213pub trait RemoteMemoryRegion {
1214    #[doc(alias = "Collective")]
1215    /// Allocate a shared memory region from the asymmetric heap.
1216    /// There will be `size` number of `T` elements on each PE.
1217    ///
1218    /// Note: If there is not enough memory in the lamellar heap on the calling PE
1219    /// this call will trigger a "heap grow" operation (initiated and handled by the runtime),
1220    /// this behavior can be disabled by setting the env variable "LAMELLAR_HEAP_MODE=static",
1221    /// in which case this call will cause a panic if there is not enough memory.
1222    ///
1223    /// Alternatively, you can use the `try_alloc_shared_mem_region` method which returns
1224    /// a `Result` and allows you to handle the error case when there is not enough memory.
1225    ///
1226    /// # Collective Operation
1227    /// Requires all PEs associated with the `array` to enter the call otherwise deadlock will occur (i.e. team barriers are being called internally)
1228    ///
1229    fn alloc_shared_mem_region<T: Dist + std::marker::Sized>(
1230        &self,
1231        size: usize,
1232    ) -> SharedMemoryRegionHandle<T>;
1233
1234    #[doc(alias = "Collective")]
1235    /// Allocate a shared memory region from the asymmetric heap.
1236    /// There will be `size` number of `T` elements on each PE.
1237    ///
1238    /// # Collective Operation
1239    /// Requires all PEs associated with the `array` to enter the call otherwise deadlock will occur (i.e. team barriers are being called internally)
1240    ///
1241    fn try_alloc_shared_mem_region<T: Dist + std::marker::Sized>(
1242        &self,
1243        size: usize,
1244    ) -> FallibleSharedMemoryRegionHandle<T>;
1245
1246    #[doc(alias("One-sided", "onesided"))]
1247    /// Allocate a one-sided memory region from the internal lamellar heap.
1248    /// This region only exists on the calling PE, but the returned handle can be
1249    /// sent to other PEs allowing remote access to the region.
1250    /// There will be `size` number of `T` elements on the calling PE.
1251    ///
1252    /// Note: If there is not enough memory in the lamellar heap on the calling PE
1253    /// this call will trigger a "heap grow" operation (initiated and handled by the runtime),
1254    /// this behavior can be disabled by setting the env variable "LAMELLAR_HEAP_MODE=static",
1255    /// in which case this call will cause a panic if there is not enough memory.
1256    ///
1257    /// Alternatively, you can use the `try_alloc_one_sided_mem_region` method which returns
1258    /// a `Result` and allows you to handle the error case when there is not enough memory.
1259    ///
1260    /// # One-sided Operation
1261    /// the calling PE will allocate the memory region locally, without intervention from the other PEs.
1262    ///
1263    fn alloc_one_sided_mem_region<T: Dist + std::marker::Sized>(
1264        &self,
1265        size: usize,
1266    ) -> OneSidedMemoryRegion<T>;
1267
1268    #[doc(alias("One-sided", "onesided"))]
1269    /// Allocate a one-sided memory region from the internal lamellar heap.
1270    /// This region only exists on the calling PE, but the returned handle can be
1271    /// sent to other PEs allowing remote access to the region.
1272    /// There will be `size` number of `T` elements on the calling PE.
1273    ///
1274    /// # One-sided Operation
1275    /// the calling PE will allocate the memory region locally, without intervention from the other PEs.
1276    ///
1277    fn try_alloc_one_sided_mem_region<T: Dist + std::marker::Sized>(
1278        &self,
1279        size: usize,
1280    ) -> Result<OneSidedMemoryRegion<T>, anyhow::Error>;
1281}
1282
1283impl<T: Dist> Drop for MemoryRegion<T> {
1284    //#[tracing::instrument(skip_all)]
1285    fn drop(&mut self) {
1286        // println!("trying to dropping mem region {:?}",self);
1287        if self.addr != 0 {
1288            match self.mode {
1289                Mode::Local => self.rdma.rt_free(self.addr), // - self.rdma.base_addr());
1290                Mode::Shared => self.rdma.free(self.addr),
1291                Mode::Remote => {}
1292            }
1293        }
1294        // println!("dropping mem region {:?}",self);
1295    }
1296}
1297
1298impl<T: Dist> std::fmt::Debug for MemoryRegion<T> {
1299    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1300        // write!(f, "{:?}", slice)
1301        write!(
1302            f,
1303            "addr {:#x} size {:?} backend {:?}", // cnt: {:?}",
1304            self.addr,
1305            self.size,
1306            self.backend,
1307            // self.cnt.load(Ordering::SeqCst)
1308        )
1309    }
1310}