use crate::mach_sys::*; use crate::AudioThreadPriorityError; use libc::{pthread_self, pthread_t}; use log::info; use mach::kern_return::{kern_return_t, KERN_SUCCESS}; use mach::mach_time::{mach_timebase_info, mach_timebase_info_data_t}; use mach::message::mach_msg_type_number_t; use mach::port::mach_port_t; use std::mem::size_of; extern "C" { fn pthread_mach_thread_np(tid: pthread_t) -> mach_port_t; // Those functions are commented out in thread_policy.h but somehow it works just fine !? fn thread_policy_set( thread: thread_t, flavor: thread_policy_flavor_t, policy_info: thread_policy_t, count: mach_msg_type_number_t, ) -> kern_return_t; fn thread_policy_get( thread: thread_t, flavor: thread_policy_flavor_t, policy_info: thread_policy_t, count: &mut mach_msg_type_number_t, get_default: &mut boolean_t, ) -> kern_return_t; } // can't use size_of in const fn just now in stable, use a macro for now. macro_rules! THREAD_TIME_CONSTRAINT_POLICY_COUNT { () => { (size_of::() / size_of::()) as u32 }; } #[derive(Debug)] pub struct RtPriorityHandleInternal { tid: mach_port_t, previous_time_constraint_policy: thread_time_constraint_policy_data_t, } impl Default for RtPriorityHandleInternal { fn default() -> Self { Self::new() } } impl RtPriorityHandleInternal { pub fn new() -> RtPriorityHandleInternal { RtPriorityHandleInternal { tid: 0, previous_time_constraint_policy: thread_time_constraint_policy_data_t { period: 0, computation: 0, constraint: 0, preemptible: 0, }, } } } pub fn demote_current_thread_from_real_time_internal( rt_priority_handle: RtPriorityHandleInternal, ) -> Result<(), AudioThreadPriorityError> { unsafe { let mut h = rt_priority_handle; let rv: kern_return_t = thread_policy_set( h.tid, THREAD_TIME_CONSTRAINT_POLICY, (&mut h.previous_time_constraint_policy) as *mut _ as thread_policy_t, THREAD_TIME_CONSTRAINT_POLICY_COUNT!(), ); if rv != KERN_SUCCESS { return Err(AudioThreadPriorityError::new( "thread demotion error: thread_policy_get: RT", )); } info!("thread {} priority restored.", h.tid); } Ok(()) } pub fn promote_current_thread_to_real_time_internal( audio_buffer_frames: u32, audio_samplerate_hz: u32, ) -> Result { let mut rt_priority_handle = RtPriorityHandleInternal::new(); let buffer_frames = if audio_buffer_frames > 0 { audio_buffer_frames } else { audio_samplerate_hz / 20 }; unsafe { let tid: mach_port_t = pthread_mach_thread_np(pthread_self()); let mut time_constraints = thread_time_constraint_policy_data_t { period: 0, computation: 0, constraint: 0, preemptible: 0, }; // Get current thread attributes, to revert back to the correct setting later if needed. rt_priority_handle.tid = tid; // false: we want to get the current value, not the default value. If this is `false` after // returning, it means there are no current settings because of other factor, and the // default was returned instead. let mut get_default: boolean_t = 0; let mut count: mach_msg_type_number_t = THREAD_TIME_CONSTRAINT_POLICY_COUNT!(); let mut rv: kern_return_t = thread_policy_get( tid, THREAD_TIME_CONSTRAINT_POLICY, (&mut time_constraints) as *mut _ as thread_policy_t, &mut count, &mut get_default, ); if rv != KERN_SUCCESS { return Err(AudioThreadPriorityError::new( "thread promotion error: thread_policy_get: time_constraint", )); } rt_priority_handle.previous_time_constraint_policy = time_constraints; let cb_duration = buffer_frames as f32 / (audio_samplerate_hz as f32) * 1000.; // The multiplicators are somwhat arbitrary for now. let mut timebase_info = mach_timebase_info_data_t { denom: 0, numer: 0 }; mach_timebase_info(&mut timebase_info); let ms2abs: f32 = ((timebase_info.denom as f32) / timebase_info.numer as f32) * 1000000.; // Computation time is half of constraint, per macOS 12 behaviour. time_constraints = thread_time_constraint_policy_data_t { period: (cb_duration * ms2abs) as u32, computation: (cb_duration / 2.0 * ms2abs) as u32, constraint: (cb_duration * ms2abs) as u32, preemptible: 1, // true }; rv = thread_policy_set( tid, THREAD_TIME_CONSTRAINT_POLICY, (&mut time_constraints) as *mut _ as thread_policy_t, THREAD_TIME_CONSTRAINT_POLICY_COUNT!(), ); if rv != KERN_SUCCESS { return Err(AudioThreadPriorityError::new( "thread promotion error: thread_policy_set: time_constraint", )); } info!("thread {} bumped to real time priority.", tid); } Ok(rt_priority_handle) }