// Copyright 2022 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::os::windows::raw::HANDLE; use log::warn; use winapi::shared::minwindef::FALSE; use winapi::um::avrt::AvRevertMmThreadCharacteristics; use winapi::um::avrt::AvSetMmThreadCharacteristicsA; use winapi::um::errhandlingapi::GetLastError; use winapi::um::processthreadsapi::GetCurrentThread; use winapi::um::processthreadsapi::SetThreadPriority; use super::errno_result; use super::Result; pub fn set_audio_thread_priority() -> Result { // SAFETY: // Safe because we know Pro Audio is part of windows and we down task_index. let multimedia_handle = unsafe { let mut task_index: u32 = 0; // "Pro Audio" is defined in: // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows // NT\CurrentVersion\Multimedia\SystemProfile\Tasks\Pro Audio let pro_audio = std::ffi::CString::new("Pro Audio").unwrap(); AvSetMmThreadCharacteristicsA(pro_audio.as_ptr(), &mut task_index) }; if multimedia_handle.is_null() { warn!( "Failed to set audio thread to Pro Audio. Error: {}", // SAFETY: trivially safe unsafe { GetLastError() } ); errno_result() } else { Ok(SafeMultimediaHandle { multimedia_handle }) } } pub fn set_thread_priority(thread_priority: i32) -> Result<()> { let res = // SAFETY: // Safe because priority level value is valid and a valid thread handle will be passed in unsafe { SetThreadPriority(GetCurrentThread(), thread_priority) }; if res == 0 { errno_result() } else { Ok(()) } } pub struct SafeMultimediaHandle { multimedia_handle: HANDLE, } impl Drop for SafeMultimediaHandle { fn drop(&mut self) { // SAFETY: // Safe because we `multimedia_handle` is defined in the same thread and is created in the // function above. `multimedia_handle` needs be created from // `AvSetMmThreadCharacteristicsA`. This will also drop the `mulitmedia_handle`. if unsafe { AvRevertMmThreadCharacteristics(self.multimedia_handle) } == FALSE { warn!( "Failed to revert audio thread. Error: {}", // SAFETY: trivially safe unsafe { GetLastError() } ); } } } #[cfg(test)] mod test { use winapi::um::processthreadsapi::GetCurrentThread; use winapi::um::processthreadsapi::GetThreadPriority; use winapi::um::winbase::THREAD_PRIORITY_NORMAL; use winapi::um::winbase::THREAD_PRIORITY_TIME_CRITICAL; use super::*; // TODO(b/223733375): Enable ignored flaky tests. #[test] #[ignore] fn test_mm_handle_is_dropped() { // SAFETY: // Safe because the only the only unsafe functions called are to get the thread // priority. unsafe { let thread_priority = GetThreadPriority(GetCurrentThread()); assert_eq!(thread_priority, THREAD_PRIORITY_NORMAL as i32); { let _handle = set_audio_thread_priority(); let thread_priority = GetThreadPriority(GetCurrentThread()); assert_eq!(thread_priority, THREAD_PRIORITY_TIME_CRITICAL as i32); } let thread_priority = GetThreadPriority(GetCurrentThread()); assert_eq!(thread_priority, THREAD_PRIORITY_NORMAL as i32); } } }