Skip to content

Add opt-in realtime-like thread priority for the pacer and audio threads#10

Merged
LinuxJedi merged 1 commit into
mainfrom
realtime-priority
Jun 19, 2026
Merged

Add opt-in realtime-like thread priority for the pacer and audio threads#10
LinuxJedi merged 1 commit into
mainfrom
realtime-priority

Conversation

@LinuxJedi

Copy link
Copy Markdown
Owner

Summary

Adds a best-effort, off-by-default scheduling-priority feature for Copperline's two latency-critical host threads — the wall-clock pacer (the main thread) and the cpal audio callback. On a busy host the scheduler can preempt either at the wrong moment, showing up as frame stutter or audio underruns; raising their priority reduces both. It never changes emulated behaviour — like all pacing, it only schedules host work.

Per-platform handling

"Real-time priority" is portable in neither API nor semantics, so the new src/priority.rs handles each platform on its own terms:

  • macOS — the pacer thread joins the USER_INTERACTIVE QoS class (pthread_set_qos_class_self_np), the idiomatic unprivileged low-latency request. The audio callback is left alone: Core Audio already runs it on a real-time thread, and pinning a QoS class onto it would only demote it. Uses the libc QoS API already in the tree, so no new macOS build deps.
  • Windows — both threads are raised to THREAD_PRIORITY_HIGHEST via the thread-priority crate; no privilege required.
  • Linux/other Unix — raising priority needs privilege (an rtprio rlimit, CAP_SYS_NICE, or root); without it the request is logged and declined, and the thread keeps normal scheduling.

Dependency notes

  • thread-priority is scoped to cfg(not(target_os = "macos")), so it (and the windows-sys it pulls in) never enters the macOS build.
  • audio_thread_priority was deliberately avoided — it drags libdbus into Linux builds for RtKit, which would require packaging/CI changes.

Configuration

Gated by [emulation] realtime_priority (default false), overridable for one run by the COPPERLINE_REALTIME_PRIORITY env var (0/false/off forces off; anything else forces on). The pacer is elevated only when actually pacing (skipped for unthrottled headless captures); the audio thread promotes itself once on its first callback (latched, so steady-state audio does no extra work).

Docs

  • docs/guide/configuration.md — the [emulation] reference.
  • docs/internals/timing.md — a new Thread scheduling priority subsection under Real-time pacing.
  • docs/internals/architecture.md — the threading model and the priority.rs source-layout entry.
  • copperline.example.toml — the commented key.

Test plan

  • cargo fmt --check, cargo clippy --all-targets --all-features --locked -- -D warnings, cargo build --locked — all clean (the exact CI commands).
  • Full suite passes (1004 tests, including 2 new ones in priority.rs that exercise the real macOS QoS syscall and assert the wrappers are panic-free on every platform).
  • Live smoke test on macOS confirmed the audio path logs audio thread left as-is (Core Audio runs it real-time) and makes the correct call.
  • ⚠️ The interactive pacer path (QoS elevation in a windowed run) was not driven end-to-end locally — it needs a real window — but it compiles, is wired in main(), and uses the same syscall the unit test exercises directly.

@LinuxJedi LinuxJedi force-pushed the realtime-priority branch from b98d521 to 194c2f3 Compare June 19, 2026 06:29
Introduce a best-effort, off-by-default scheduling-priority feature for
Copperline's two latency-critical host threads: the wall-clock pacer (the
main thread) and the cpal audio callback. On a busy host the scheduler can
preempt either at the wrong moment, showing up as frame stutter or audio
underruns; raising their priority reduces both. It never changes emulated
behaviour -- like all pacing, it only schedules host work.

"Real-time priority" is portable in neither API nor semantics, so the new
src/priority.rs handles each platform on its own terms:

- macOS: the pacer thread joins the USER_INTERACTIVE QoS class via
  pthread_set_qos_class_self_np (the idiomatic unprivileged low-latency
  request). The audio callback is left alone -- Core Audio already runs it
  on a real-time thread, and pinning a QoS class onto it would only demote
  it. Uses the libc QoS API already in the tree, so no macOS build deps.
- Windows: both threads are raised to THREAD_PRIORITY_HIGHEST via the
  thread-priority crate; no privilege required.
- Linux/other Unix: raising priority needs privilege (rtprio rlimit,
  CAP_SYS_NICE, or root); without it the request is logged and declined.

thread-priority is scoped to cfg(not(target_os = "macos")) so it never
enters the macOS build. audio_thread_priority was deliberately avoided: it
pulls libdbus into Linux builds for RtKit, which would need packaging/CI
changes.

The feature is gated by [emulation] realtime_priority (default false),
overridable for one run by COPPERLINE_REALTIME_PRIORITY. The pacer is
elevated only when actually pacing (skipped for unthrottled headless
captures); the audio thread promotes itself once on its first callback.

Documented in the configuration guide, the example config, and the timing
and architecture internals. Covered by unit tests that exercise the real
macOS QoS syscall and assert the wrappers are panic-free on every platform.
@LinuxJedi LinuxJedi force-pushed the realtime-priority branch from 194c2f3 to c1a2db8 Compare June 19, 2026 06:33
@LinuxJedi LinuxJedi merged commit 52a2605 into main Jun 19, 2026
6 checks passed
@LinuxJedi LinuxJedi deleted the realtime-priority branch June 19, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant