use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use crate::sys::{fi_close, fid_mr};
pub(crate) struct Registration {
memory_region: *mut fid_mr,
}
unsafe impl Send for Registration {}
unsafe impl Sync for Registration {}
impl Registration {
pub(crate) fn new(memory_region: *mut fid_mr) -> Arc<Self> {
Arc::new(Self { memory_region })
}
pub(crate) fn memory_region(&self) -> *mut fid_mr {
self.memory_region
}
}
impl std::fmt::Debug for Registration {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Registration")
.field("memory_region", &self.memory_region)
.finish()
}
}
impl Drop for Registration {
fn drop(&mut self) {
unsafe { fi_close(&mut (*self.memory_region).fid) };
}
}
static REGISTRY: Mutex<Vec<DomainRegions>> = Mutex::new(Vec::new());
struct DomainRegions {
domain: usize,
regions: BTreeMap<usize, RegionEntry>,
}
struct RegionEntry {
end: usize,
registration: Arc<Registration>,
}
pub(crate) fn track(base: usize, end: usize, domain: usize, registration: &Arc<Registration>) {
let replaced = {
let mut registry = lock();
domain_regions(&mut registry, domain).insert(
base,
RegionEntry {
end,
registration: Arc::clone(registration),
},
)
};
drop(replaced);
}
pub(crate) fn covering(start: usize, end: usize, domain: usize) -> Option<Arc<Registration>> {
let registry = lock();
let regions = ®istry
.iter()
.find(|entry| entry.domain == domain)?
.regions;
regions
.range(..=start)
.next_back()
.filter(|(base, entry)| **base <= start && end <= entry.end)
.map(|(_, entry)| Arc::clone(&entry.registration))
}
pub fn invalidate(start: usize, end: usize) {
let retired: Vec<Arc<Registration>> = {
let mut registry = lock();
let mut retired = Vec::new();
for domain in registry.iter_mut() {
let bases: Vec<usize> = domain
.regions
.range(..end)
.filter(|(_, entry)| start < entry.end)
.map(|(&base, _)| base)
.collect();
for base in bases {
if let Some(entry) = domain.regions.remove(&base) {
retired.push(entry.registration);
}
}
}
retired
};
drop(retired);
}
pub(crate) fn clear_domain(domain: usize) {
let retired: Vec<Arc<Registration>> = {
let mut registry = lock();
match registry.iter().position(|entry| entry.domain == domain) {
Some(index) => registry
.remove(index)
.regions
.into_values()
.map(|entry| entry.registration)
.collect(),
None => Vec::new(),
}
};
drop(retired);
}
fn domain_regions(
registry: &mut Vec<DomainRegions>,
domain: usize,
) -> &mut BTreeMap<usize, RegionEntry> {
let index = match registry.iter().position(|entry| entry.domain == domain) {
Some(index) => index,
None => {
registry.push(DomainRegions {
domain,
regions: BTreeMap::new(),
});
registry.len() - 1
}
};
&mut registry[index].regions
}
fn lock() -> std::sync::MutexGuard<'static, Vec<DomainRegions>> {
REGISTRY
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::{Registration, clear_domain, covering, invalidate, track};
use crate::operands::CacheableSpan;
use crate::sys::{
FI_READ, FI_REMOTE_READ, FI_REMOTE_WRITE, FI_WRITE, fi_allocinfo, fi_close, fi_domain,
fi_ep_type_FI_EP_RDM, fi_fabric, fi_freeinfo, fi_getinfo, fi_info, fi_mr_reg, fi_version,
fid_domain, fid_fabric, fid_mr,
};
use std::ffi::CString;
use std::os::raw::c_void;
use std::ptr;
use std::sync::Arc;
struct Domain {
domain: *mut fid_domain,
fabric: *mut fid_fabric,
info: *mut fi_info,
next_key: std::cell::Cell<u64>,
}
impl Domain {
fn open() -> Self {
let provider = CString::new("tcp").expect("provider name");
let mut info: *mut fi_info = ptr::null_mut();
let mut fabric: *mut fid_fabric = ptr::null_mut();
let mut domain: *mut fid_domain = ptr::null_mut();
unsafe {
let hints = fi_allocinfo();
assert!(!hints.is_null(), "fi_allocinfo");
(*(*hints).ep_attr).type_ = fi_ep_type_FI_EP_RDM;
(*(*hints).fabric_attr).prov_name = provider.as_ptr().cast_mut();
let code = fi_getinfo(
fi_version(),
ptr::null(),
ptr::null(),
0,
hints,
&raw mut info,
);
(*(*hints).fabric_attr).prov_name = ptr::null_mut();
fi_freeinfo(hints);
assert_eq!(0, code, "fi_getinfo(tcp)");
assert_eq!(
0,
fi_fabric((*info).fabric_attr, &raw mut fabric, ptr::null_mut()),
"fi_fabric"
);
assert_eq!(
0,
fi_domain(fabric, info, &raw mut domain, ptr::null_mut()),
"fi_domain"
);
}
Self {
domain,
fabric,
info,
next_key: std::cell::Cell::new(1),
}
}
fn key(&self) -> usize {
self.domain as usize
}
fn register(&self, memory: &mut [u8]) -> *mut fid_mr {
let key = self.next_key.get();
self.next_key.set(key + 1);
let mut region: *mut fid_mr = ptr::null_mut();
let code = unsafe {
fi_mr_reg(
self.domain,
memory.as_ptr().cast::<c_void>(),
memory.len(),
u64::from(FI_READ | FI_WRITE | FI_REMOTE_READ | FI_REMOTE_WRITE),
0,
key,
0,
&raw mut region,
ptr::null_mut(),
)
};
assert_eq!(0, code, "fi_mr_reg");
region
}
}
impl Drop for Domain {
fn drop(&mut self) {
clear_domain(self.key());
unsafe {
fi_close(&mut (*self.domain).fid);
fi_close(&mut (*self.fabric).fid);
fi_freeinfo(self.info);
}
}
}
#[test]
fn freeing_one_value_deregisters_a_neighbour_that_is_still_in_flight() {
let domain = Domain::open();
let mut extent = vec![0u8; 64 * 1024];
let base = extent.as_mut_ptr() as usize;
let registration = Registration::new(domain.register(&mut extent));
track(base, base + extent.len(), domain.key(), ®istration);
drop(registration);
let operand = (base + 4096, base + 8192);
let lease = covering(operand.0, operand.1, domain.key())
.expect("the cached registration covers this operand");
invalidate(base + 32768, base + 36864);
assert!(
covering(operand.0, operand.1, domain.key()).is_none(),
"a reclaimed extent must leave the registry immediately"
);
assert_eq!(
1,
Arc::strong_count(&lease),
"the in-flight operand must be the last holder, not a holder of a closed region"
);
}
#[test]
fn a_cacheable_span_widens_the_registration() {
const EXTENT: usize = 64 * 1024;
let domain = Domain::open();
let mut regions = crate::local_regions::LocalRegions::new(domain.domain);
const OPERAND: usize = 4096;
let mut memory = vec![0u8; 3 * EXTENT];
let start = (memory.as_mut_ptr() as usize).next_multiple_of(EXTENT);
let pointer = start as *mut u8;
let span = CacheableSpan::new(start, EXTENT).expect("a span containing the operand");
regions
.operand(pointer, OPERAND, || Some(span))
.expect("register the widened span");
let neighbour = regions
.operand((start + EXTENT - OPERAND) as *mut u8, OPERAND, || {
panic!("a cache hit must not ask for a span")
})
.expect("an operand elsewhere in the span");
let cached = covering(start, start + OPERAND, domain.key()).expect("the span is tracked");
assert!(
Arc::ptr_eq(
&neighbour.lease.expect("a tracked span always leases"),
&cached
),
"operands across the span must share one registration"
);
let outside = start + 2 * EXTENT;
let bogus = CacheableSpan::new(outside + EXTENT, OPERAND).expect("a span past the operand");
regions
.operand(outside as *mut u8, OPERAND, || Some(bogus))
.expect("falls back to the operand's own extent");
assert!(
covering(outside + EXTENT, outside + EXTENT + OPERAND, domain.key()).is_none(),
"a span not containing the operand must not be registered"
);
}
#[test]
fn a_registration_handed_to_a_caller_has_no_lease() {
let domain = Domain::open();
let mut extent = vec![0u8; 8192];
let base = extent.as_mut_ptr() as usize;
let registration = Registration::new(domain.register(&mut extent));
track(base, base + extent.len(), domain.key(), ®istration);
drop(registration);
let lease = covering(base, base + extent.len(), domain.key()).expect("just tracked");
invalidate(base, base + extent.len());
assert!(covering(base, base + extent.len(), domain.key()).is_none());
assert_eq!(
1,
Arc::strong_count(&lease),
"the caller's lease must outlive the registry's, not be closed underneath it"
);
}
#[test]
fn a_pinned_span_serves_the_operands_inside_it() {
let domain = Domain::open();
let mut arena = vec![0u8; 64 * 1024];
let base = arena.as_mut_ptr() as usize;
let mut regions = crate::local_regions::LocalRegions::new(domain.domain);
regions.pin(base, arena.len()).expect("pin the arena");
let operand = regions
.operand((base + 4096) as *mut u8, 4096, || None)
.expect("operand inside a pinned span");
let lease = operand.lease.expect("a pinned span always leases");
let cached = covering(base + 4096, base + 8192, domain.key()).expect("still registered");
assert!(
Arc::ptr_eq(&lease, &cached),
"the operand must reuse the pinned registration"
);
}
#[test]
fn dropping_a_region_retires_its_registration() {
let domain = Domain::open();
let mut arena = vec![0u8; 8192];
let base = arena.as_mut_ptr() as usize;
let end = base + arena.len();
let mut regions = crate::local_regions::LocalRegions::new(domain.domain);
regions.pin(base, arena.len()).expect("pin the arena");
let lease = covering(base, end, domain.key()).expect("just pinned");
let region = crate::MemoryRegion::new(Box::new(arena), base, end);
drop(region);
assert!(
covering(base, end, domain.key()).is_none(),
"a dropped region must leave the registry immediately"
);
assert_eq!(
1,
Arc::strong_count(&lease),
"an operand mid-flight must be the last holder, not a holder of a closed region"
);
}
#[test]
fn a_region_outliving_its_endpoint_closes_nothing_twice() {
let domain = Domain::open();
let mut arena = vec![0u8; 8192];
let base = arena.as_mut_ptr() as usize;
let end = base + arena.len();
let mut regions = crate::local_regions::LocalRegions::new(domain.domain);
regions.pin(base, arena.len()).expect("pin the arena");
clear_domain(domain.key());
assert!(covering(base, end, domain.key()).is_none());
drop(crate::MemoryRegion::new(Box::new(arena), base, end));
assert!(covering(base, end, domain.key()).is_none());
}
#[test]
fn descriptors_stay_valid_while_reclaims_run_underneath() {
let domain = Domain::open();
let key = domain.key();
let mut extents: Vec<Vec<u8>> = (0..8).map(|_| vec![0u8; 32 * 1024]).collect();
let bounds: Vec<(usize, usize)> = extents
.iter_mut()
.map(|extent| {
let base = extent.as_mut_ptr() as usize;
(base, base + extent.len())
})
.collect();
for (index, extent) in extents.iter_mut().enumerate() {
let registration = Registration::new(domain.register(extent));
track(bounds[index].0, bounds[index].1, key, ®istration);
}
let running = Arc::new(std::sync::atomic::AtomicBool::new(true));
let readers: Vec<_> = (0..4)
.map(|_| {
let bounds = bounds.clone();
let running = Arc::clone(&running);
std::thread::spawn(move || {
let mut seen = 0usize;
while running.load(std::sync::atomic::Ordering::Relaxed) {
for (base, end) in &bounds {
if let Some(lease) = covering(*base, *base + 4096, key) {
let descriptor =
unsafe { crate::sys::fi_mr_desc(lease.memory_region()) };
assert!(!lease.memory_region().is_null());
let _ = descriptor;
seen += 1;
}
let _ = end;
}
}
seen
})
})
.collect();
for _ in 0..60 {
for (base, end) in &bounds {
invalidate(*base, *end);
}
for (index, extent) in extents.iter_mut().enumerate() {
let registration = Registration::new(domain.register(extent));
track(bounds[index].0, bounds[index].1, key, ®istration);
}
}
running.store(false, std::sync::atomic::Ordering::Relaxed);
let total: usize = readers
.into_iter()
.map(|reader| reader.join().unwrap_or(0))
.sum();
assert!(0 < total, "the readers should have leased something");
}
}