use std::sync::Arc;
type Keepalive<'a> = Arc<dyn Send + Sync + 'a>;
#[derive(Clone)]
pub struct HostPin<'a> {
#[allow(dead_code)]
keepalive: Keepalive<'a>,
ptr: *mut u8,
len: usize,
}
unsafe impl Send for HostPin<'_> {}
unsafe impl Sync for HostPin<'_> {}
impl<'a> HostPin<'a> {
pub(crate) fn new(keepalive: Keepalive<'a>, ptr: *mut u8, len: usize) -> Self {
HostPin {
keepalive,
ptr,
len,
}
}
#[cfg(feature = "static")]
pub(crate) fn narrowed(mut self, len: usize) -> Self {
self.len = self.len.min(len);
self
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
pub fn as_mut_ptr(&self) -> *mut u8 {
self.ptr
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn alignment(&self) -> usize {
let addr = self.ptr as usize;
if addr == 0 {
return 0;
}
1usize << addr.trailing_zeros()
}
pub unsafe fn as_slice(&self) -> &[u8] {
if self.ptr.is_null() || self.len == 0 {
return &[];
}
unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
}
}
#[cfg(all(unix, feature = "static"))]
pub(crate) struct MmapOwner {
ptr: std::ptr::NonNull<std::ffi::c_void>,
len: usize,
}
#[cfg(all(unix, feature = "static"))]
unsafe impl Send for MmapOwner {}
#[cfg(all(unix, feature = "static"))]
unsafe impl Sync for MmapOwner {}
#[cfg(all(unix, feature = "static"))]
impl MmapOwner {
pub(crate) fn new(ptr: std::ptr::NonNull<std::ffi::c_void>, len: usize) -> Self {
MmapOwner { ptr, len }
}
pub(crate) fn base(&self) -> *mut u8 {
self.ptr.as_ptr() as *mut u8
}
}
#[cfg(all(unix, feature = "static"))]
static CPU_MAPPING_GUARD: std::sync::RwLock<()> = std::sync::RwLock::new(());
#[cfg(all(unix, feature = "static"))]
pub(crate) fn cpu_mapping_shared() -> std::sync::RwLockReadGuard<'static, ()> {
CPU_MAPPING_GUARD.read().unwrap_or_else(|e| e.into_inner())
}
#[doc(hidden)]
#[cfg(feature = "static")]
pub fn with_cpu_mappings_excluded<R>(f: impl FnOnce() -> R) -> R {
#[cfg(unix)]
let _guard = CPU_MAPPING_GUARD.write().unwrap_or_else(|e| e.into_inner());
f()
}
#[doc(hidden)]
#[cfg(feature = "dynamic")]
pub fn with_cpu_mappings_excluded<F: FnOnce() -> R, R>(f: F) -> R {
struct Call<F, R> {
f: Option<F>,
out: Option<std::thread::Result<R>>,
}
unsafe extern "C" fn trampoline<F: FnOnce() -> R, R>(ctx: *mut std::ffi::c_void) {
let call = unsafe { &mut *ctx.cast::<Call<F, R>>() };
if let Some(f) = call.f.take() {
call.out = Some(std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)));
}
}
let mut call = Call {
f: Some(f),
out: None,
};
unsafe {
edgefirst_tensor_ffi::ef_tensor_with_cpu_mappings_excluded(
Some(trampoline::<F, R>),
(&mut call as *mut Call<F, R>).cast(),
)
};
match call
.out
.expect("ef_tensor_with_cpu_mappings_excluded skipped its callback")
{
Ok(r) => r,
Err(payload) => std::panic::resume_unwind(payload),
}
}
#[cfg(all(unix, feature = "static"))]
impl Drop for MmapOwner {
#[cfg_attr(test, mutants::skip)]
fn drop(&mut self) {
unsafe {
let _ = nix::sys::mman::munmap(self.ptr, self.len);
}
}
}
impl std::fmt::Debug for HostPin<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HostPin")
.field("ptr", &self.ptr)
.field("len", &self.len)
.field("alignment", &self.alignment())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pin_at(addr: usize, len: usize) -> HostPin<'static> {
HostPin::new(Arc::new(()), addr as *mut u8, len)
}
#[test]
fn is_empty_tracks_the_length() {
assert!(pin_at(0x1000, 0).is_empty());
assert!(!pin_at(0x1000, 1).is_empty());
}
#[test]
fn alignment_is_the_largest_power_of_two_dividing_the_address() {
assert_eq!(pin_at(0, 8).alignment(), 0);
assert_eq!(pin_at(0x6, 8).alignment(), 2);
assert_eq!(pin_at(0x1040, 8).alignment(), 64);
assert_eq!(pin_at(0x1000, 8).alignment(), 4096);
let top = 1usize << (usize::BITS - 1);
assert_eq!(pin_at(top, 8).alignment(), top);
}
#[test]
fn debug_names_the_type_and_its_extent() {
let text = format!("{:?}", pin_at(0x1040, 7));
assert!(text.contains("HostPin"), "{text}");
assert!(text.contains("len: 7"), "{text}");
assert!(text.contains("alignment: 64"), "{text}");
}
}