native-ipc 0.6.0

One safe API for least-authority native shared memory: sealed memfd on Linux, Mach memory entries on macOS, exact-rights sections on Windows
use super::*;
use static_assertions::{assert_impl_all, assert_not_impl_any};

assert_impl_all!(ResourceOwner: Send);
assert_not_impl_any!(ResourceOwner: Sync, Clone);
assert_impl_all!(RegionLease: Send, Sync);
assert_not_impl_any!(RegionLease: Clone);
assert_impl_all!(LeaseReservation: Send);
assert_not_impl_any!(LeaseReservation: Sync, Clone);

fn limits() -> SessionLimits {
    SessionLimits {
        max_active_regions: 2,
        max_active_bytes: 10,
        ..SessionLimits::default()
    }
}

#[test]
fn leases_charge_exact_limits_and_block_recoverable_close() {
    let mut owner = ResourceOwner::new(limits()).unwrap();
    let rollback = owner.reserve(1).unwrap();
    drop(rollback);
    assert_eq!(owner.active_lease_facts().regions, 0);
    let first = owner.reserve(4).unwrap().complete_for_test(4).unwrap();
    let second = owner.reserve(6).unwrap().complete_for_test(6).unwrap();
    assert!(matches!(owner.reserve(1), Err(ResourceError::ActiveLimit)));
    assert_eq!(
        owner.try_close(),
        Err(ResourceError::ActiveLeases(ActiveLeaseFacts {
            regions: 2,
            bytes: 10,
            consistency: LeaseFactsConsistency::ApproximateDuringConcurrentDrop,
        }))
    );
    drop(first);
    assert_eq!(
        owner.active_lease_facts(),
        ActiveLeaseFacts {
            regions: 1,
            bytes: 6,
            consistency: LeaseFactsConsistency::ApproximateDuringConcurrentDrop,
        }
    );
    drop(second);
    owner.try_close().unwrap();
    assert_eq!(owner.state(), LivenessState::Closed);
    assert!(matches!(owner.reserve(1), Err(ResourceError::Closed)));
}

#[test]
fn poison_is_shared_with_live_regions_and_close_waits_for_drop() {
    let mut owner = ResourceOwner::new(limits()).unwrap();
    let lease = owner.reserve(5).unwrap().complete_for_test(5).unwrap();
    owner.poison();
    assert_eq!(owner.state(), LivenessState::Poisoned);
    assert_eq!(lease.state(), LivenessState::Poisoned);
    assert!(matches!(owner.reserve(1), Err(ResourceError::Poisoned)));
    assert!(matches!(
        owner.try_close(),
        Err(ResourceError::ActiveLeases(_))
    ));
    drop(lease);
    owner.try_close().unwrap();
    assert_eq!(owner.state(), LivenessState::Closed);
}

#[test]
fn dropping_control_owner_marks_surviving_region_hostile() {
    let lease = {
        let mut owner = ResourceOwner::new(limits()).unwrap();
        owner.reserve(1).unwrap().complete_for_test(1).unwrap()
    };
    assert_eq!(lease.state(), LivenessState::Poisoned);
    drop(lease);

    let mut invalid = limits();
    invalid.max_active_regions = 0;
    assert!(matches!(
        ResourceOwner::new(invalid),
        Err(ResourceError::InvalidLimits)
    ));
}

#[test]
fn concurrent_mapping_drops_never_permit_premature_close() {
    use std::sync::{Arc, Barrier};

    let mut owner = ResourceOwner::new(limits()).unwrap();
    let first = owner.reserve(4).unwrap().complete_for_test(4).unwrap();
    let second = owner.reserve(6).unwrap().complete_for_test(6).unwrap();
    let barrier = Arc::new(Barrier::new(3));
    let first_barrier = Arc::clone(&barrier);
    let first_drop = std::thread::spawn(move || {
        first_barrier.wait();
        drop(first);
    });
    let second_barrier = Arc::clone(&barrier);
    let second_drop = std::thread::spawn(move || {
        second_barrier.wait();
        drop(second);
    });
    assert!(matches!(
        owner.try_close(),
        Err(ResourceError::ActiveLeases(_))
    ));
    barrier.wait();
    first_drop.join().unwrap();
    second_drop.join().unwrap();
    assert_eq!(
        owner.active_lease_facts(),
        ActiveLeaseFacts {
            regions: 0,
            bytes: 0,
            consistency: LeaseFactsConsistency::Exact,
        }
    );
    owner.try_close().unwrap();
}

#[test]
fn completion_rechecks_exact_mapping_length_and_liveness() {
    let mut owner = ResourceOwner::new(limits()).unwrap();
    let mismatch = owner.reserve(4).unwrap().complete_for_test(3);
    assert!(matches!(
        mismatch,
        Err(ResourceError::MappedLengthMismatch {
            reserved: 4,
            actual: 3
        })
    ));
    assert_eq!(owner.active_lease_facts().regions, 0);

    let reservation = owner.reserve(4).unwrap();
    owner.poison();
    assert!(matches!(
        reservation.complete_for_test(4),
        Err(ResourceError::Poisoned)
    ));
    assert_eq!(owner.active_lease_facts().regions, 0);
}