Source code

Revision control

Copy as Markdown

Other Tools

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::<thread_time_constraint_policy_data_t>() / size_of::<integer_t>()) 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<RtPriorityHandleInternal, AudioThreadPriorityError> {
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)
}