Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,39 @@ jobs:
sudo prlimit --pid $$ --rtprio=10:10
echo "RLIMIT_RTPRIO soft=$(ulimit -Sr) hard=$(ulimit -Hr)"
rustup run ${{ matrix.rust }} cargo test --no-default-features

# musl defines pthread_t as a pointer instead of an integer, unlike glibc.
build-musl:
runs-on: ubuntu-24.04
strategy:
matrix:
rust: [stable, nightly]

steps:
- uses: actions/checkout@v2

- name: Install Rust (musl target)
run: |
rustup toolchain install ${{ matrix.rust }} --profile minimal
rustup target add x86_64-unknown-linux-musl --toolchain ${{ matrix.rust }}

- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install libdbus-1-dev

- name: Build (musl, with dbus)
shell: bash
# The crate is a staticlib/rlib, so this doesn't link, and hence needs no musl libdbus: only
# the headers, which pkg-config finds for the host.
run: PKG_CONFIG_ALLOW_CROSS=1 rustup run ${{ matrix.rust }} cargo build --target x86_64-unknown-linux-musl --features dbus

- name: Build (musl, without dbus)
shell: bash
run: rustup run ${{ matrix.rust }} cargo build --target x86_64-unknown-linux-musl --no-default-features

- name: Test (musl, without dbus)
shell: bash
# Statically linked musl test binaries run fine on the glibc runner. As in the fallback job,
# the native promotion path needs permission to request real-time scheduling.
run: |
sudo prlimit --pid $$ --rtprio=10:10
rustup run ${{ matrix.rust }} cargo test --target x86_64-unknown-linux-musl --no-default-features -- --nocapture
6 changes: 4 additions & 2 deletions src/rt_linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ impl RtPriorityThreadInfoInternal {
/// struct, so no uninitialized padding bytes are ever read. Any trailing padding stays zero.
pub fn serialize(&self) -> [u8; std::mem::size_of::<Self>()] {
let thread_id = self.thread_id.to_ne_bytes();
let pthread_id = self.pthread_id.to_ne_bytes();
// `pthread_t` is an integer with glibc but a pointer with musl: go through `usize`, which
// is as wide as both.
let pthread_id = (self.pthread_id as usize).to_ne_bytes();
let pid = self.pid.to_ne_bytes();
let policy = self.policy.to_ne_bytes();
let priority = self.priority.to_ne_bytes();
Expand Down Expand Up @@ -91,7 +93,7 @@ impl RtPriorityThreadInfoInternal {
let mut src = bytes.iter().copied();
RtPriorityThreadInfoInternal {
thread_id: kernel_pid_t::from_ne_bytes(take(&mut src)),
pthread_id: libc::pthread_t::from_ne_bytes(take(&mut src)),
pthread_id: usize::from_ne_bytes(take(&mut src)) as libc::pthread_t,
pid: libc::pid_t::from_ne_bytes(take(&mut src)),
policy: libc::c_int::from_ne_bytes(take(&mut src)),
priority: libc::c_int::from_ne_bytes(take(&mut src)),
Expand Down
32 changes: 26 additions & 6 deletions src/rt_linux_native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ impl RtPriorityThreadInfoInternal {
/// struct, so no uninitialized padding bytes are ever read. Any trailing padding stays zero.
pub fn serialize(&self) -> [u8; std::mem::size_of::<Self>()] {
let thread_id = self.thread_id.to_ne_bytes();
let pthread_id = self.pthread_id.to_ne_bytes();
// `pthread_t` is an integer with glibc but a pointer with musl: go through `usize`, which
// is as wide as both.
let pthread_id = (self.pthread_id as usize).to_ne_bytes();
let pid = self.pid.to_ne_bytes();
let policy = self.policy.to_ne_bytes();
let priority = self.priority.to_ne_bytes();
Expand Down Expand Up @@ -107,7 +109,7 @@ impl RtPriorityThreadInfoInternal {
let mut src = bytes.iter().copied();
RtPriorityThreadInfoInternal {
thread_id: kernel_pid_t::from_ne_bytes(take(&mut src)),
pthread_id: libc::pthread_t::from_ne_bytes(take(&mut src)),
pthread_id: usize::from_ne_bytes(take(&mut src)) as libc::pthread_t,
pid: libc::pid_t::from_ne_bytes(take(&mut src)),
policy: libc::c_int::from_ne_bytes(take(&mut src)),
priority: libc::c_int::from_ne_bytes(take(&mut src)),
Expand Down Expand Up @@ -140,6 +142,26 @@ fn sched_error(context: &str) -> AudioThreadPriorityError {
AudioThreadPriorityError::new(&format!("{}: {}", context, OSError::last_os_error()))
}

/// Set the scheduling policy and priority of the thread with tid `tid`.
///
/// This goes through the syscall directly because musl's `sched_setscheduler` is a stub that always
/// fails with `ENOSYS`: POSIX specifies it as process-scoped, which Linux does not provide. The
/// syscall is per-thread, which is what is needed here.
fn sched_setscheduler(
tid: libc::pid_t,
policy: libc::c_int,
param: &libc::sched_param,
) -> libc::c_long {
unsafe {
libc::syscall(
libc::SYS_sched_setscheduler,
tid,
policy,
param as *const libc::sched_param,
)
}
}

/// A thread's system-wide tid narrowed to `pid_t` for the scheduler syscalls. A tid always fits in
/// `pid_t` (it is a pid), but convert defensively rather than truncating.
fn scheduler_tid(thread_id: kernel_pid_t) -> Result<libc::pid_t, AudioThreadPriorityError> {
Expand Down Expand Up @@ -236,8 +258,7 @@ pub fn promote_thread_to_real_time_internal(
let mut param = unsafe { std::mem::zeroed::<libc::sched_param>() };
param.sched_priority = requested_priority();

let rc =
unsafe { libc::sched_setscheduler(tid, libc::SCHED_FIFO | SCHED_RESET_ON_FORK, &param) };
let rc = sched_setscheduler(tid, libc::SCHED_FIFO | SCHED_RESET_ON_FORK, &param);
if rc < 0 {
return Err(sched_error("could not promote thread"));
}
Expand All @@ -254,8 +275,7 @@ pub fn demote_thread_from_real_time_internal(
let tid = scheduler_tid(thread_info.thread_id)?;
let mut param = unsafe { std::mem::zeroed::<libc::sched_param>() };
param.sched_priority = thread_info.priority;
let rc =
unsafe { libc::sched_setscheduler(tid, thread_info.policy | SCHED_RESET_ON_FORK, &param) };
let rc = sched_setscheduler(tid, thread_info.policy | SCHED_RESET_ON_FORK, &param);
if rc < 0 {
return Err(sched_error("could not demote thread"));
}
Expand Down
Loading