summaryrefslogtreecommitdiffstats
path: root/third_party/rust/audio_thread_priority
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/audio_thread_priority/.cargo-checksum.json2
-rw-r--r--third_party/rust/audio_thread_priority/Cargo.toml4
-rw-r--r--third_party/rust/audio_thread_priority/src/lib.rs9
-rw-r--r--third_party/rust/audio_thread_priority/src/rt_win.rs233
4 files changed, 207 insertions, 41 deletions
diff --git a/third_party/rust/audio_thread_priority/.cargo-checksum.json b/third_party/rust/audio_thread_priority/.cargo-checksum.json
index e1a42d3fe7..fc49e6d1a8 100644
--- a/third_party/rust/audio_thread_priority/.cargo-checksum.json
+++ b/third_party/rust/audio_thread_priority/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"Cargo.toml":"6bceb8b41d1646bd255b39c13dcca5bbcc0984f0f2aca73decb7ee5627443387","LICENSE":"32ee9dbf6196874fc9d406c54a888a6c4cbb9aa4a7f35b46befeaff43a78fe85","Makefile":"0f9a771cfb30c7c4b9961d82fdca4e9e229a955bb2e636474a4101389e18e938","README.md":"c123692b3b50dd621b896a8269814d609cbf1e532b461bf4a77854ddd607eb7a","atp_test.cpp":"8075a040941a65fb9e3f7cbf0535853ca6661c3ac442ec35569b42b24bbec797","audio_thread_priority.h":"f0ecaf1b674f794cde0dc834028e074d4e4675d22ae96acf08b2ae1dceb3474e","generate_osx_bindings.sh":"06e4e03450f788ced18d31fff5660919e6f6ec1119ddace363ffeb82f0518a71","src/lib.rs":"1d5c629d1ee2dab80f7bf422a3add07eaca203f5c1557fdd379361d632e14224","src/mach_sys.rs":"352560fcb9b41d877cff92e5b3b04d6dc68b1f30508ce4b9aed78940120a883e","src/rt_linux.rs":"e77db6d66fb76772dde6c12850ef2c50730cfe00decc0c67211e63f3e8a59c9f","src/rt_mach.rs":"29f8c0397f14cecbac1f76394c2abfe0e05903b54486cf735f9a94a10c168643","src/rt_win.rs":"f2ba097cebf65252c27d9d6dcfbe1fcc041c4b312724bd98e080e114a4de3bb6"},"package":"17fd24082f432a11ad4fde564af75fc9a0beef2f4199e7691b090ff0e065c4d0"} \ No newline at end of file
+{"files":{"Cargo.toml":"2dd36097338035819ea7fdb1de6d37009571d761fb659194423f7143a3ba25c7","LICENSE":"32ee9dbf6196874fc9d406c54a888a6c4cbb9aa4a7f35b46befeaff43a78fe85","Makefile":"0f9a771cfb30c7c4b9961d82fdca4e9e229a955bb2e636474a4101389e18e938","README.md":"c123692b3b50dd621b896a8269814d609cbf1e532b461bf4a77854ddd607eb7a","atp_test.cpp":"8075a040941a65fb9e3f7cbf0535853ca6661c3ac442ec35569b42b24bbec797","audio_thread_priority.h":"f0ecaf1b674f794cde0dc834028e074d4e4675d22ae96acf08b2ae1dceb3474e","generate_osx_bindings.sh":"06e4e03450f788ced18d31fff5660919e6f6ec1119ddace363ffeb82f0518a71","src/lib.rs":"142a4f40a0deba0544673c5c8cb4398525a7ead2cc5addb4e69dd9a81e771ded","src/mach_sys.rs":"352560fcb9b41d877cff92e5b3b04d6dc68b1f30508ce4b9aed78940120a883e","src/rt_linux.rs":"e77db6d66fb76772dde6c12850ef2c50730cfe00decc0c67211e63f3e8a59c9f","src/rt_mach.rs":"29f8c0397f14cecbac1f76394c2abfe0e05903b54486cf735f9a94a10c168643","src/rt_win.rs":"0c85d771c544e4bb2b8fb60bdffe5a3080c112896f42360eb92cd3b85c010ca0"},"package":"e3632611da7e79f8fc8fd75840f1ccfa7792dbf1e25d00791344a4450dd8834f"} \ No newline at end of file
diff --git a/third_party/rust/audio_thread_priority/Cargo.toml b/third_party/rust/audio_thread_priority/Cargo.toml
index 30861cdafd..e1fa1a342f 100644
--- a/third_party/rust/audio_thread_priority/Cargo.toml
+++ b/third_party/rust/audio_thread_priority/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "audio_thread_priority"
-version = "0.31.0"
+version = "0.32.0"
authors = ["Paul Adenot <paul@paul.cx>"]
description = "Bump a thread to real-time priority, for audio work, on Linux, Windows and macOS"
readme = "README.md"
@@ -61,5 +61,5 @@ version = "0.3"
version = "0.52"
features = [
"Win32_Foundation",
- "Win32_System_Threading",
+ "Win32_System_LibraryLoader",
]
diff --git a/third_party/rust/audio_thread_priority/src/lib.rs b/third_party/rust/audio_thread_priority/src/lib.rs
index eaae33d9d1..6006f7966e 100644
--- a/third_party/rust/audio_thread_priority/src/lib.rs
+++ b/third_party/rust/audio_thread_priority/src/lib.rs
@@ -647,6 +647,15 @@ mod tests {
// automatically deallocated, but not demoted until the thread exits.
}
}
+
+ #[test]
+ fn it_works_in_different_threads() {
+ let handles: Vec<_> = (0..32).map(|_| std::thread::spawn(it_works)).collect();
+ for handle in handles {
+ handle.join().unwrap()
+ }
+ }
+
cfg_if! {
if #[cfg(target_os = "linux")] {
use nix::unistd::*;
diff --git a/third_party/rust/audio_thread_priority/src/rt_win.rs b/third_party/rust/audio_thread_priority/src/rt_win.rs
index 3a2bbf29d9..480c5bd75c 100644
--- a/third_party/rust/audio_thread_priority/src/rt_win.rs
+++ b/third_party/rust/audio_thread_priority/src/rt_win.rs
@@ -1,14 +1,11 @@
-use windows_sys::s;
-use windows_sys::Win32::Foundation::GetLastError;
-use windows_sys::Win32::Foundation::FALSE;
-use windows_sys::Win32::Foundation::HANDLE;
-use windows_sys::Win32::System::Threading::{
- AvRevertMmThreadCharacteristics, AvSetMmThreadCharacteristicsA,
-};
-
+use self::avrt_lib::AvRtLibrary;
use crate::AudioThreadPriorityError;
-
use log::info;
+use std::sync::OnceLock;
+use windows_sys::{
+ w,
+ Win32::Foundation::{HANDLE, WIN32_ERROR},
+};
#[derive(Debug)]
pub struct RtPriorityHandleInternal {
@@ -17,7 +14,7 @@ pub struct RtPriorityHandleInternal {
}
impl RtPriorityHandleInternal {
- pub fn new(mmcss_task_index: u32, task_handle: HANDLE) -> RtPriorityHandleInternal {
+ fn new(mmcss_task_index: u32, task_handle: HANDLE) -> RtPriorityHandleInternal {
RtPriorityHandleInternal {
mmcss_task_index,
task_handle,
@@ -25,45 +22,205 @@ impl RtPriorityHandleInternal {
}
}
+fn avrt() -> Result<&'static AvRtLibrary, AudioThreadPriorityError> {
+ static AV_RT_LIBRARY: OnceLock<Result<AvRtLibrary, WIN32_ERROR>> = OnceLock::new();
+ AV_RT_LIBRARY
+ .get_or_init(AvRtLibrary::try_new)
+ .as_ref()
+ .map_err(|win32_error| {
+ AudioThreadPriorityError::new(&format!("Unable to load avrt.dll ({win32_error})"))
+ })
+}
+
+pub fn promote_current_thread_to_real_time_internal(
+ _audio_buffer_frames: u32,
+ _audio_samplerate_hz: u32,
+) -> Result<RtPriorityHandleInternal, AudioThreadPriorityError> {
+ avrt()?
+ .set_mm_thread_characteristics(w!("Audio"))
+ .map(|(mmcss_task_index, task_handle)| {
+ info!("task {mmcss_task_index} bumped to real time priority.");
+ RtPriorityHandleInternal::new(mmcss_task_index, task_handle)
+ })
+ .map_err(|win32_error| {
+ AudioThreadPriorityError::new(&format!(
+ "Unable to bump the thread priority ({win32_error})"
+ ))
+ })
+}
+
pub fn demote_current_thread_from_real_time_internal(
rt_priority_handle: RtPriorityHandleInternal,
) -> Result<(), AudioThreadPriorityError> {
- let rv = unsafe { AvRevertMmThreadCharacteristics(rt_priority_handle.task_handle) };
- if rv == FALSE {
- return Err(AudioThreadPriorityError::new(&format!(
- "Unable to restore the thread priority ({:?})",
- unsafe { GetLastError() }
- )));
+ let RtPriorityHandleInternal {
+ mmcss_task_index,
+ task_handle,
+ } = rt_priority_handle;
+ avrt()?
+ .revert_mm_thread_characteristics(task_handle)
+ .map(|_| {
+ info!("task {mmcss_task_index} priority restored.");
+ })
+ .map_err(|win32_error| {
+ AudioThreadPriorityError::new(&format!(
+ "Unable to restore the thread priority for task {mmcss_task_index} ({win32_error})"
+ ))
+ })
+}
+
+mod avrt_lib {
+ use super::win32_utils::{win32_error_if, OwnedLibrary};
+ use std::sync::Once;
+ use windows_sys::{
+ core::PCWSTR,
+ s, w,
+ Win32::Foundation::{BOOL, FALSE, HANDLE, WIN32_ERROR},
+ };
+
+ type AvSetMmThreadCharacteristicsWFn = unsafe extern "system" fn(PCWSTR, *mut u32) -> HANDLE;
+ type AvRevertMmThreadCharacteristicsFn = unsafe extern "system" fn(HANDLE) -> BOOL;
+
+ #[derive(Debug)]
+ pub(super) struct AvRtLibrary {
+ // This field is never read because only used for its Drop behavior
+ #[allow(dead_code)]
+ module: OwnedLibrary,
+
+ av_set_mm_thread_characteristics_w: AvSetMmThreadCharacteristicsWFn,
+ av_revert_mm_thread_characteristics: AvRevertMmThreadCharacteristicsFn,
}
- info!(
- "task {} priority restored.",
- rt_priority_handle.mmcss_task_index
- );
+ impl AvRtLibrary {
+ pub(super) fn try_new() -> Result<Self, WIN32_ERROR> {
+ let module = OwnedLibrary::try_new(w!("avrt.dll"))?;
+ let av_set_mm_thread_characteristics_w = unsafe {
+ std::mem::transmute::<_, AvSetMmThreadCharacteristicsWFn>(
+ module.get_proc(s!("AvSetMmThreadCharacteristicsW"))?,
+ )
+ };
+ let av_revert_mm_thread_characteristics = unsafe {
+ std::mem::transmute::<_, AvRevertMmThreadCharacteristicsFn>(
+ module.get_proc(s!("AvRevertMmThreadCharacteristics"))?,
+ )
+ };
+ Ok(Self {
+ module,
+ av_set_mm_thread_characteristics_w,
+ av_revert_mm_thread_characteristics,
+ })
+ }
+
+ pub(super) fn set_mm_thread_characteristics(
+ &self,
+ task_name: PCWSTR,
+ ) -> Result<(u32, HANDLE), WIN32_ERROR> {
+ // Ensure that the first call never runs in parallel with other calls. This
+ // seems necessary to guarantee the success of these other calls. We saw them
+ // fail with an error code of ERROR_PATH_NOT_FOUND in tests, presumably on a
+ // machine where the MMCSS service was initially inactive.
+ static FIRST_CALL: Once = Once::new();
+ let mut first_call_result = None;
+ FIRST_CALL.call_once(|| {
+ first_call_result = Some(self.set_mm_thread_characteristics_internal(task_name))
+ });
+ first_call_result
+ .unwrap_or_else(|| self.set_mm_thread_characteristics_internal(task_name))
+ }
+
+ fn set_mm_thread_characteristics_internal(
+ &self,
+ task_name: PCWSTR,
+ ) -> Result<(u32, HANDLE), WIN32_ERROR> {
+ let mut mmcss_task_index = 0u32;
+ let task_handle = unsafe {
+ (self.av_set_mm_thread_characteristics_w)(task_name, &mut mmcss_task_index)
+ };
+ win32_error_if(task_handle == 0)?;
+ Ok((mmcss_task_index, task_handle))
+ }
- Ok(())
+ pub(super) fn revert_mm_thread_characteristics(
+ &self,
+ handle: HANDLE,
+ ) -> Result<(), WIN32_ERROR> {
+ let rv = unsafe { (self.av_revert_mm_thread_characteristics)(handle) };
+ win32_error_if(rv == FALSE)
+ }
+ }
}
-pub fn promote_current_thread_to_real_time_internal(
- _audio_buffer_frames: u32,
- _audio_samplerate_hz: u32,
-) -> Result<RtPriorityHandleInternal, AudioThreadPriorityError> {
- let mut task_index = 0u32;
+mod win32_utils {
+ use windows_sys::{
+ core::{PCSTR, PCWSTR},
+ Win32::{
+ Foundation::{FreeLibrary, GetLastError, HMODULE, WIN32_ERROR},
+ System::LibraryLoader::{GetProcAddress, LoadLibraryW},
+ },
+ };
+
+ pub(super) fn win32_error_if(condition: bool) -> Result<(), WIN32_ERROR> {
+ if condition {
+ Err(unsafe { GetLastError() })
+ } else {
+ Ok(())
+ }
+ }
+
+ #[derive(Debug)]
+ pub(super) struct OwnedLibrary(HMODULE);
+
+ impl OwnedLibrary {
+ pub(super) fn try_new(lib_file_name: PCWSTR) -> Result<Self, WIN32_ERROR> {
+ let module = unsafe { LoadLibraryW(lib_file_name) };
+ win32_error_if(module == 0)?;
+ Ok(Self(module))
+ }
- let handle = unsafe { AvSetMmThreadCharacteristicsA(s!("Audio"), &mut task_index) };
- let handle = RtPriorityHandleInternal::new(task_index, handle);
+ fn raw(&self) -> HMODULE {
+ self.0
+ }
- if handle.task_handle == 0 {
- return Err(AudioThreadPriorityError::new(&format!(
- "Unable to restore the thread priority ({:?})",
- unsafe { GetLastError() }
- )));
+ /// SAFETY: The caller must transmute the value wrapped in a Ok(_) to the correct
+ /// function type, with the correct extern specifier.
+ pub(super) unsafe fn get_proc(
+ &self,
+ proc_name: PCSTR,
+ ) -> Result<unsafe extern "system" fn() -> isize, WIN32_ERROR> {
+ let proc = unsafe { GetProcAddress(self.raw(), proc_name) };
+ win32_error_if(proc.is_none())?;
+ Ok(proc.unwrap())
+ }
}
- info!(
- "task {} bumped to real time priority.",
- handle.mmcss_task_index
- );
+ impl Drop for OwnedLibrary {
+ fn drop(&mut self) {
+ unsafe {
+ FreeLibrary(self.raw());
+ }
+ }
+ }
+}
- Ok(handle)
+#[cfg(test)]
+mod tests {
+ use super::{
+ avrt, demote_current_thread_from_real_time_internal,
+ promote_current_thread_to_real_time_internal,
+ };
+
+ #[test]
+ fn test_successful_avrt_library_load() {
+ assert!(avrt().is_ok())
+ }
+
+ #[test]
+ fn test_successful_api_use() {
+ let handle = promote_current_thread_to_real_time_internal(512, 44100);
+ println!("handle: {handle:?}");
+ assert!(handle.is_ok());
+
+ let result = demote_current_thread_from_real_time_internal(handle.unwrap());
+ println!("result: {result:?}");
+ assert!(result.is_ok());
+ }
}