use std::ffi::c_int;
use std::fmt;
use std::marker::PhantomData;
use std::ptr::NonNull;
use crate::protocol::{NativeRegionSpec, PeerAccess, TransferProvenance};
use native_ipc_core::layout::{
LayoutError, RegionSetLayout, ValidatedRegionLayout, ValidationExpectations,
};
use native_ipc_core::mapping::{
BindingError, ReadOnlyMapping, ReaderRegion, SoleWriterMapping, WriterRegion,
};
pub mod bootstrap;
pub(super) mod supervisor;
pub(super) mod supervisor_client;
pub(super) mod supervisor_watchdog;
pub(crate) unsafe fn run_fixed_broker_gate_process(installed_path: &std::ffi::CStr) -> ! {
unsafe { supervisor::broker_entry::run_fixed_gate_process(installed_path) }
}
pub(crate) unsafe fn run_fixed_broker_process(
installed_path: &std::ffi::CStr,
launcher_path: &std::ffi::CStr,
auth_worker_path: &std::ffi::CStr,
) -> ! {
unsafe {
supervisor::broker_entry::run_fixed_broker_process(
installed_path,
launcher_path,
auth_worker_path,
)
}
}
pub(crate) unsafe fn run_fixed_launcher_process(installed_path: &std::ffi::CStr) -> ! {
unsafe { supervisor::launcher_entry::run_fixed_launcher_process(installed_path) }
}
pub(crate) unsafe fn run_fixed_auth_worker_process(
installed_path: &std::ffi::CStr,
requirement: &std::ffi::CStr,
code_identity: [u8; 32],
) -> ! {
unsafe {
supervisor::auth_adapter::auth_worker_entry::run_fixed_auth_worker_process(
installed_path,
requirement,
code_identity,
)
}
}
type KernReturn = c_int;
type MachPort = u32;
type MachVmAddress = u64;
type MachVmSize = u64;
type MemoryObjectOffset = u64;
type MemoryObjectSize = u64;
type VmInherit = u32;
type VmProt = c_int;
const KERN_SUCCESS: KernReturn = 0;
const MACH_PORT_NULL: MachPort = 0;
const VM_FLAGS_FIXED: c_int = 0;
const VM_FLAGS_ANYWHERE: c_int = 1;
const VM_FLAGS_OVERWRITE: c_int = 0x4000;
const VM_PROT_NONE: VmProt = 0;
const VM_PROT_READ: VmProt = 1;
const VM_PROT_WRITE: VmProt = 2;
const VM_PROT_EXECUTE: VmProt = 4;
const MAP_MEM_VM_SHARE: VmProt = 0x0040_0000;
const VM_INHERIT_NONE: VmInherit = 2;
unsafe extern "C" {
static mach_task_self_: MachPort;
fn getpagesize() -> c_int;
fn mach_vm_allocate(
target: MachPort,
address: *mut MachVmAddress,
size: MachVmSize,
flags: c_int,
) -> KernReturn;
fn mach_vm_deallocate(target: MachPort, address: MachVmAddress, size: MachVmSize)
-> KernReturn;
fn mach_vm_protect(
target_task: MachPort,
address: MachVmAddress,
size: MachVmSize,
set_maximum: c_int,
new_protection: VmProt,
) -> KernReturn;
fn mach_make_memory_entry_64(
target_task: MachPort,
size: *mut MemoryObjectSize,
offset: MemoryObjectOffset,
permission: VmProt,
object_handle: *mut MachPort,
parent_entry: MachPort,
) -> KernReturn;
fn mach_vm_map(
target_task: MachPort,
address: *mut MachVmAddress,
size: MachVmSize,
mask: MachVmAddress,
flags: c_int,
object: MachPort,
offset: MemoryObjectOffset,
copy: c_int,
current_protection: VmProt,
maximum_protection: VmProt,
inheritance: VmInherit,
) -> KernReturn;
fn mach_port_deallocate(task: MachPort, name: MachPort) -> KernReturn;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MachError {
ZeroSize,
SizeOverflow {
requested: usize,
},
InvalidViewSize {
requested: usize,
region: usize,
},
InvalidPageSize(c_int),
InvalidAddress(MachVmAddress),
NullMemoryEntry,
UnexpectedEntrySize {
expected: usize,
actual: u64,
},
Kernel {
operation: &'static str,
code: KernReturn,
},
}
#[derive(Debug)]
pub enum MacBindingError {
Layout(LayoutError),
Mach(MachError),
Binding(BindingError),
Bootstrap(bootstrap::BootstrapError),
ForeignPending,
}
impl fmt::Display for MacBindingError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "Mach/core binding failed: {self:?}")
}
}
impl std::error::Error for MacBindingError {}
impl From<LayoutError> for MacBindingError {
fn from(value: LayoutError) -> Self {
Self::Layout(value)
}
}
impl From<MachError> for MacBindingError {
fn from(value: MachError) -> Self {
Self::Mach(value)
}
}
impl From<BindingError> for MacBindingError {
fn from(value: BindingError) -> Self {
Self::Binding(value)
}
}
impl From<bootstrap::BootstrapError> for MacBindingError {
fn from(value: bootstrap::BootstrapError) -> Self {
Self::Bootstrap(value)
}
}
impl fmt::Display for MachError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "Mach shared memory operation failed: {self:?}")
}
}
impl std::error::Error for MachError {}
#[derive(Debug)]
pub struct QuiescentRegion {
mapping: Mapping,
logical_len: usize,
}
impl QuiescentRegion {
pub fn new(len: usize) -> Result<Self, MachError> {
let page_size = page_size()?;
let mapped_len = page_align(len, page_size)?;
let task = current_task();
let mut mapping = Mapping::allocate(task, mapped_len)?;
unsafe { mapping.bytes_mut(mapped_len) }.fill(0);
mapping.protect(VM_PROT_READ | VM_PROT_WRITE, false)?;
mapping.protect(VM_PROT_READ | VM_PROT_WRITE, true)?;
Ok(Self {
mapping,
logical_len: len,
})
}
pub const fn len(&self) -> usize {
self.mapping.mapped_len
}
pub const fn logical_len(&self) -> usize {
self.logical_len
}
pub const fn is_empty(&self) -> bool {
false
}
pub fn as_bytes(&self) -> &[u8] {
unsafe { self.mapping.bytes(self.mapping.mapped_len) }
}
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
unsafe { self.mapping.bytes_mut(self.mapping.mapped_len) }
}
pub fn into_local_writer(self, expected_len: usize) -> Result<LocalWriterRegion, MachError> {
self.validate_transition_size(expected_len)?;
let peer_entry = MemoryEntry::<ReadOnlyCapability>::new(self.mapping.task, &self.mapping)?;
Ok(LocalWriterRegion {
mapping: self.mapping,
peer_entry,
len: expected_len,
})
}
pub fn into_remote_writer(
mut self,
expected_len: usize,
) -> Result<RemoteWriterRegion, MachError> {
self.validate_transition_size(expected_len)?;
let peer_entry = MemoryEntry::<ReadWriteCapability>::new(self.mapping.task, &self.mapping)?;
self.mapping.protect(VM_PROT_READ, false)?;
self.mapping.protect(VM_PROT_READ, true)?;
Ok(RemoteWriterRegion {
mapping: self.mapping,
peer_entry,
len: expected_len,
})
}
fn validate_transition_size(&self, expected_len: usize) -> Result<(), MachError> {
if expected_len == self.mapping.mapped_len && expected_len != 0 {
Ok(())
} else {
Err(MachError::InvalidViewSize {
requested: expected_len,
region: self.mapping.mapped_len,
})
}
}
pub fn into_bound_local_writer(
self,
expected: ValidationExpectations,
topology: RegionSetLayout,
) -> Result<WriterRegion<MacWriterMapping>, MacBindingError> {
let layout =
unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
let capability_len = self.len();
let region = self.into_local_writer(capability_len)?;
Ok(
WriterRegion::new(MacWriterMapping { region }, layout, topology)
.map_err(|(_, error)| error)?,
)
}
pub fn into_bound_remote_writer(
self,
expected: ValidationExpectations,
topology: RegionSetLayout,
) -> Result<ReaderRegion<MacReaderMapping>, MacBindingError> {
let layout =
unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
let capability_len = self.len();
let region = self.into_remote_writer(capability_len)?;
Ok(
ReaderRegion::new(MacReaderMapping { region }, layout, topology)
.map_err(|(_, error)| error)?,
)
}
pub fn transfer_local_writer(
self,
native: NativeRegionSpec,
expected: ValidationExpectations,
topology: RegionSetLayout,
channel: &mut bootstrap::ParentChannel,
) -> Result<PendingTransferredWriter, MacBindingError> {
let result = (|| {
let layout =
unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
let capability_len = self.len();
let region = self.into_local_writer(capability_len)?;
let LocalWriterRegion {
mapping,
peer_entry,
len: _,
} = region;
let runtime = WriterRegion::new(TransferredWriterMapping { mapping }, layout, topology)
.map_err(|(_, error)| error)?;
channel.send(peer_entry.name, native, PeerAccess::ReadOnly)?;
drop(peer_entry);
Ok(PendingTransferredWriter {
runtime,
provenance: channel.pending_provenance(),
})
})();
if result.is_err() {
channel.poison_transaction();
}
result
}
pub fn transfer_remote_writer(
self,
native: NativeRegionSpec,
expected: ValidationExpectations,
topology: RegionSetLayout,
channel: &mut bootstrap::ParentChannel,
) -> Result<PendingTransferredReader, MacBindingError> {
let result = (|| {
let layout =
unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
let capability_len = self.len();
let region = self.into_remote_writer(capability_len)?;
let RemoteWriterRegion {
mapping,
peer_entry,
len: _,
} = region;
let runtime = ReaderRegion::new(TransferredReaderMapping { mapping }, layout, topology)
.map_err(|(_, error)| error)?;
channel.send(peer_entry.name, native, PeerAccess::SoleWriter)?;
drop(peer_entry);
Ok(PendingTransferredReader {
runtime,
provenance: channel.pending_provenance(),
})
})();
if result.is_err() {
channel.poison_transaction();
}
result
}
}
pub struct PendingTransferredWriter {
runtime: WriterRegion<TransferredWriterMapping>,
provenance: TransferProvenance,
}
pub struct PendingTransferredReader {
runtime: ReaderRegion<TransferredReaderMapping>,
provenance: TransferProvenance,
}
pub struct PendingImportedReader {
runtime: ReaderRegion<ImportedReaderMapping>,
provenance: TransferProvenance,
}
pub struct PendingImportedWriter {
runtime: WriterRegion<ImportedWriterMapping>,
provenance: TransferProvenance,
}
pub struct TransferredWriterMapping {
mapping: Mapping,
}
unsafe impl SoleWriterMapping for TransferredWriterMapping {
fn base(&self) -> NonNull<u8> {
self.mapping.address
}
fn len(&self) -> usize {
self.mapping.mapped_len
}
}
pub struct TransferredReaderMapping {
mapping: Mapping,
}
unsafe impl ReadOnlyMapping for TransferredReaderMapping {
fn base(&self) -> NonNull<u8> {
self.mapping.address
}
fn len(&self) -> usize {
self.mapping.mapped_len
}
}
pub struct ImportedReaderMapping {
mapping: Mapping,
}
unsafe impl ReadOnlyMapping for ImportedReaderMapping {
fn base(&self) -> NonNull<u8> {
self.mapping.address
}
fn len(&self) -> usize {
self.mapping.mapped_len
}
}
pub struct ImportedWriterMapping {
mapping: Mapping,
}
unsafe impl SoleWriterMapping for ImportedWriterMapping {
fn base(&self) -> NonNull<u8> {
self.mapping.address
}
fn len(&self) -> usize {
self.mapping.mapped_len
}
}
impl bootstrap::ChildChannel {
pub fn receive_reader(
&mut self,
len: usize,
native: NativeRegionSpec,
expected: ValidationExpectations,
topology: RegionSetLayout,
) -> Result<PendingImportedReader, MacBindingError> {
let result = (|| {
let right = self.receive(native, PeerAccess::ReadOnly)?;
let mapping = Mapping::map_port(current_task(), len, right.name(), VM_PROT_READ)?;
let bytes = unsafe { mapping.bytes(len) };
let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
drop(right);
Ok(PendingImportedReader {
runtime: ReaderRegion::new(ImportedReaderMapping { mapping }, layout, topology)
.map_err(|(_, error)| error)?,
provenance: self.pending_provenance(),
})
})();
if result.is_err() {
self.poison_transaction();
}
result
}
pub fn receive_writer(
&mut self,
len: usize,
native: NativeRegionSpec,
expected: ValidationExpectations,
topology: RegionSetLayout,
) -> Result<PendingImportedWriter, MacBindingError> {
let result = (|| {
let right = self.receive(native, PeerAccess::SoleWriter)?;
let mapping = Mapping::map_port(
current_task(),
len,
right.name(),
VM_PROT_READ | VM_PROT_WRITE,
)?;
let bytes = unsafe { mapping.bytes(len) };
let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
drop(right);
Ok(PendingImportedWriter {
runtime: WriterRegion::new(ImportedWriterMapping { mapping }, layout, topology)
.map_err(|(_, error)| error)?,
provenance: self.pending_provenance(),
})
})();
if result.is_err() {
self.poison_transaction();
}
result
}
}
impl bootstrap::ParentChannel {
pub fn commit_transfers(
&mut self,
writer: PendingTransferredWriter,
reader: PendingTransferredReader,
) -> Result<
(
WriterRegion<TransferredWriterMapping>,
ReaderRegion<TransferredReaderMapping>,
),
MacBindingError,
> {
let expected = self.pending_provenance();
if writer.provenance != expected || reader.provenance != expected {
self.poison_transaction();
return Err(MacBindingError::ForeignPending);
}
self.ready_and_commit()?;
Ok((writer.runtime, reader.runtime))
}
}
impl bootstrap::ChildChannel {
pub fn commit_imports(
&mut self,
reader: PendingImportedReader,
writer: PendingImportedWriter,
) -> Result<
(
ReaderRegion<ImportedReaderMapping>,
WriterRegion<ImportedWriterMapping>,
),
MacBindingError,
> {
let expected = self.pending_provenance();
if reader.provenance != expected || writer.provenance != expected {
self.poison_transaction();
return Err(MacBindingError::ForeignPending);
}
self.ready_and_wait_commit()?;
Ok((reader.runtime, writer.runtime))
}
}
pub struct MacWriterMapping {
region: LocalWriterRegion,
}
unsafe impl SoleWriterMapping for MacWriterMapping {
fn base(&self) -> NonNull<u8> {
self.region.mapping.address
}
fn len(&self) -> usize {
self.region.mapping.mapped_len
}
}
pub struct MacReaderMapping {
region: RemoteWriterRegion,
}
unsafe impl ReadOnlyMapping for MacReaderMapping {
fn base(&self) -> NonNull<u8> {
self.region.mapping.address
}
fn len(&self) -> usize {
self.region.mapping.mapped_len
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct LocalWriterRegion {
mapping: Mapping,
peer_entry: MemoryEntry<ReadOnlyCapability>,
len: usize,
}
impl LocalWriterRegion {
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct RemoteWriterRegion {
mapping: Mapping,
peer_entry: MemoryEntry<ReadWriteCapability>,
len: usize,
}
impl RemoteWriterRegion {
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
#[derive(Debug)]
struct Mapping {
task: MachPort,
address: NonNull<u8>,
mapped_len: usize,
reservation_address: MachVmAddress,
reservation_len: usize,
guarded: bool,
}
unsafe impl Send for Mapping {}
impl Mapping {
fn allocate(task: MachPort, mapped_len: usize) -> Result<Self, MachError> {
let mut address = 0;
let result = unsafe {
mach_vm_allocate(
task,
&mut address,
mapped_len as MachVmSize,
VM_FLAGS_ANYWHERE,
)
};
check_kernel("mach_vm_allocate", result)?;
Self::from_allocated(task, address, mapped_len)
}
fn map_port(
task: MachPort,
mapped_len: usize,
port: MachPort,
protection: VmProt,
) -> Result<Self, MachError> {
debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
if let Some(guarded) = Self::map_port_guarded(task, mapped_len, port, protection) {
return Ok(guarded);
}
let mut address = 0;
let result = unsafe {
mach_vm_map(
task,
&mut address,
mapped_len as MachVmSize,
0,
VM_FLAGS_ANYWHERE,
port,
0,
0,
protection,
protection,
VM_INHERIT_NONE,
)
};
check_kernel("mach_vm_map", result)?;
Self::from_allocated(task, address, mapped_len)
}
fn map_port_guarded(
task: MachPort,
mapped_len: usize,
port: MachPort,
protection: VmProt,
) -> Option<Self> {
let page = page_size().ok()?;
if mapped_len == 0 || !mapped_len.is_multiple_of(page) {
return None;
}
let total = mapped_len.checked_add(page.checked_mul(2)?)?;
if total > isize::MAX as usize {
return None;
}
let mut reservation: MachVmAddress = 0;
let result = unsafe {
mach_vm_allocate(
task,
&mut reservation,
total as MachVmSize,
VM_FLAGS_ANYWHERE,
)
};
if result != KERN_SUCCESS {
return None;
}
let interior_target = reservation + page as MachVmAddress;
for band in [reservation, interior_target + mapped_len as MachVmAddress] {
let result =
unsafe { mach_vm_protect(task, band, page as MachVmSize, 0, VM_PROT_NONE) };
if result != KERN_SUCCESS {
deallocate_mapping(task, reservation, total);
return None;
}
}
let mut interior = interior_target;
let result = unsafe {
mach_vm_map(
task,
&mut interior,
mapped_len as MachVmSize,
0,
VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
port,
0,
0,
protection,
protection,
VM_INHERIT_NONE,
)
};
if result != KERN_SUCCESS || interior != interior_target {
deallocate_mapping(task, reservation, total);
return None;
}
let Some(address) = usize::try_from(interior)
.ok()
.and_then(|address| NonNull::new(address as *mut u8))
else {
deallocate_mapping(task, reservation, total);
return None;
};
Some(Self {
task,
address,
mapped_len,
reservation_address: reservation,
reservation_len: total,
guarded: true,
})
}
fn protect(&mut self, protection: VmProt, set_maximum: bool) -> Result<(), MachError> {
debug_assert_eq!(protection & VM_PROT_EXECUTE, 0);
let result = unsafe {
mach_vm_protect(
self.task,
self.address(),
self.mapped_len as MachVmSize,
c_int::from(set_maximum),
protection,
)
};
check_kernel("mach_vm_protect", result)
}
fn from_allocated(
task: MachPort,
address: MachVmAddress,
mapped_len: usize,
) -> Result<Self, MachError> {
let address_usize = match usize::try_from(address) {
Ok(value) => value,
Err(_) => {
deallocate_mapping(task, address, mapped_len);
return Err(MachError::InvalidAddress(address));
}
};
let reservation_address = address;
let Some(address) = NonNull::new(address_usize as *mut u8) else {
return Err(MachError::InvalidAddress(0));
};
Ok(Self {
task,
address,
mapped_len,
reservation_address,
reservation_len: mapped_len,
guarded: false,
})
}
fn address(&self) -> MachVmAddress {
self.address.as_ptr() as usize as MachVmAddress
}
unsafe fn bytes(&self, len: usize) -> &[u8] {
assert!(len <= self.mapped_len && len <= isize::MAX as usize);
unsafe { std::slice::from_raw_parts(self.address.as_ptr(), len) }
}
unsafe fn bytes_mut(&mut self, len: usize) -> &mut [u8] {
assert!(len <= self.mapped_len && len <= isize::MAX as usize);
unsafe { std::slice::from_raw_parts_mut(self.address.as_ptr(), len) }
}
}
impl Drop for Mapping {
fn drop(&mut self) {
deallocate_mapping(self.task, self.reservation_address, self.reservation_len);
#[cfg(test)]
observe_vnext_drop_for_test("mapping");
}
}
#[derive(Debug)]
struct ReadOnlyCapability;
#[derive(Debug)]
struct ReadWriteCapability;
trait CapabilityAccess {
const PROTECTION: VmProt;
}
impl CapabilityAccess for ReadOnlyCapability {
const PROTECTION: VmProt = VM_PROT_READ;
}
impl CapabilityAccess for ReadWriteCapability {
const PROTECTION: VmProt = VM_PROT_READ | VM_PROT_WRITE;
}
#[derive(Debug)]
struct MemoryEntry<Access> {
task: MachPort,
name: MachPort,
_access: PhantomData<fn() -> Access>,
}
impl<Access: CapabilityAccess> MemoryEntry<Access> {
fn new(task: MachPort, mapping: &Mapping) -> Result<Self, MachError> {
let mut entry_size = mapping.mapped_len as MemoryObjectSize;
let mut name = MACH_PORT_NULL;
let permission = Access::PROTECTION | MAP_MEM_VM_SHARE;
debug_assert_eq!(permission & VM_PROT_EXECUTE, 0);
let result = unsafe {
mach_make_memory_entry_64(
task,
&mut entry_size,
mapping.address(),
permission,
&mut name,
MACH_PORT_NULL,
)
};
if result != KERN_SUCCESS {
if name != MACH_PORT_NULL {
deallocate_port(task, name);
}
return Err(MachError::Kernel {
operation: "mach_make_memory_entry_64",
code: result,
});
}
if name == MACH_PORT_NULL {
return Err(MachError::NullMemoryEntry);
}
let entry = Self {
task,
name,
_access: PhantomData,
};
if entry_size != mapping.mapped_len as MemoryObjectSize {
return Err(MachError::UnexpectedEntrySize {
expected: mapping.mapped_len,
actual: entry_size,
});
}
Ok(entry)
}
}
impl<Access> Drop for MemoryEntry<Access> {
fn drop(&mut self) {
deallocate_port(self.task, self.name);
#[cfg(test)]
observe_vnext_drop_for_test("memory-entry");
}
}
fn current_task() -> MachPort {
unsafe { mach_task_self_ }
}
fn page_size() -> Result<usize, MachError> {
let size = unsafe { getpagesize() };
let Ok(converted) = usize::try_from(size) else {
return Err(MachError::InvalidPageSize(size));
};
if converted == 0 || !converted.is_power_of_two() {
return Err(MachError::InvalidPageSize(size));
}
Ok(converted)
}
fn page_align(size: usize, page_size: usize) -> Result<usize, MachError> {
if size == 0 {
return Err(MachError::ZeroSize);
}
let aligned = size
.checked_add(page_size - 1)
.map(|value| value & !(page_size - 1))
.ok_or(MachError::SizeOverflow { requested: size })?;
if aligned > isize::MAX as usize {
return Err(MachError::SizeOverflow { requested: size });
}
Ok(aligned)
}
fn check_kernel(operation: &'static str, code: KernReturn) -> Result<(), MachError> {
if code == KERN_SUCCESS {
Ok(())
} else {
Err(MachError::Kernel { operation, code })
}
}
fn deallocate_mapping(task: MachPort, address: MachVmAddress, mapped_len: usize) {
let _ = unsafe { mach_vm_deallocate(task, address, mapped_len as MachVmSize) };
}
fn deallocate_port(task: MachPort, name: MachPort) {
let _ = unsafe { mach_port_deallocate(task, name) };
}
#[cfg(test)]
thread_local! {
static VNEXT_DROP_OBSERVER: std::cell::RefCell<
Option<std::sync::Arc<std::sync::Mutex<Vec<&'static str>>>>
> = const { std::cell::RefCell::new(None) };
}
#[cfg(test)]
fn set_vnext_drop_observer_for_test(
observer: Option<std::sync::Arc<std::sync::Mutex<Vec<&'static str>>>>,
) {
VNEXT_DROP_OBSERVER.with(|slot| *slot.borrow_mut() = observer);
}
#[cfg(test)]
fn observe_vnext_drop_for_test(label: &'static str) {
VNEXT_DROP_OBSERVER.with(|slot| {
if let Some(observer) = slot.borrow().as_ref() {
observer.lock().unwrap().push(label);
}
});
}
#[path = "macos_vnext/image_identity.rs"]
pub(crate) mod vnext_image_identity;
#[path = "macos_vnext/memory.rs"]
pub(crate) mod vnext_memory;
#[path = "macos_vnext/transport.rs"]
pub(crate) mod vnext_transport;
#[path = "macos_vnext/session.rs"]
pub(crate) mod vnext_session;
#[cfg(test)]
#[path = "macos_vnext/image_identity_test.rs"]
mod vnext_image_identity_test;
#[cfg(test)]
#[path = "macos_vnext/memory_test.rs"]
mod vnext_memory_test;
#[cfg(test)]
#[path = "macos_vnext/transport_test.rs"]
mod vnext_transport_test;
#[cfg(test)]
#[path = "macos_vnext/session_test.rs"]
mod vnext_session_test;
#[cfg(test)]
#[path = "macos_vnext/reducer_test.rs"]
mod vnext_reducer_test;
#[cfg(test)]
#[path = "macos_test.rs"]
mod tests;