diff --git a/src/unix.rs b/src/unix.rs index 26f8944..f6aad7f 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -596,19 +596,29 @@ pub fn set_thread_priority_and_policy( e => Err(Error::OS(e)), } } else { - // If this is a normal-scheduled thread, the priority is - // set via niceness. - set_errno(0); + // Normal priority threads must be set with static priority 0. + let params = ScheduleParams { sched_priority: 0 }.into_posix(); - let ret = unsafe { libc::setpriority(libc::PRIO_PROCESS, 0, fixed_priority) }; - if ret == 0 { - return Ok(()); + let ret = unsafe { + libc::pthread_setschedparam( + native, + policy.to_posix(), + ¶ms as *const libc::sched_param, + ) + }; + + if ret != 0 { + return Err(Error::OS(ret)); } - match errno() { - 0 => Ok(()), - e => Err(Error::OS(e)), + // Normal priority threads adjust relative priority through niceness. + set_errno(0); + let ret = unsafe { libc::setpriority(libc::PRIO_PROCESS, 0, fixed_priority) }; + if ret != 0 { + return Err(Error::OS(errno())); } + + Ok(()) } } } @@ -820,6 +830,44 @@ mod tests { assert!(thread_schedule_policy_param(thread_id).is_ok()); } + // Running this test requires CAP_SYS_NICE. + #[test] + fn change_between_realtime_and_normal_policies_requires_capabilities() { + use crate::ThreadPriorityOsValue; + + const TEST_PRIORITY: u8 = 15; + + let realtime_policy = ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Fifo); + let normal_policy = ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Other); + + // While we may desire an OS-specific priority, the reported value is always crossplatform. + let desired_priority = ThreadPriority::Os(ThreadPriorityOsValue(TEST_PRIORITY as _)); + let expected_priority = ThreadPriority::Crossplatform(ThreadPriorityValue(TEST_PRIORITY)); + + let thread = std::thread::current(); + thread + .set_priority_and_policy(realtime_policy, desired_priority) + .expect("to set realtime fifo policy"); + + assert_eq!(thread.get_schedule_policy(), Ok(realtime_policy)); + assert_eq!(thread.get_priority(), Ok(expected_priority)); + + thread + .set_priority_and_policy(normal_policy, desired_priority) + .expect("to set normal other policy"); + + assert_eq!(thread.get_schedule_policy(), Ok(normal_policy)); + + // On linux, normal priority threads always have static priority 0. Instead the "nice" value is used. + #[cfg(not(target_os = "linux"))] + assert_eq!(thread.get_priority(), Ok(expected_priority)); + #[cfg(target_os = "linux")] + { + let nice = unsafe { libc::getpriority(0, 0) }; + assert_eq!(nice, TEST_PRIORITY as i32); + } + } + #[test] #[cfg(target_os = "linux")] fn set_deadline_policy() {