Still searching...

This commit is contained in:
2026-02-03 02:53:34 +01:00
parent b305df3d79
commit 5c805c60d7
5 changed files with 146 additions and 126 deletions

View File

@@ -3,11 +3,12 @@ use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use super::link::LinkState;
use super::sequencer::{AudioCommand, MidiCommand};
use super::timing::{SyncTime, ACTIVE_WAIT_THRESHOLD_US};
use super::sequencer::{set_realtime_priority, AudioCommand, MidiCommand};
use super::timing::SyncTime;
/// A command scheduled for dispatch at a specific time.
#[derive(Clone)]
@@ -47,6 +48,9 @@ impl PartialEq for TimedCommand {
impl Eq for TimedCommand {}
/// Spin-wait threshold in microseconds for dispatcher.
const SPIN_THRESHOLD_US: SyncTime = 100;
/// Main dispatcher loop - receives timed commands and dispatches them at the right moment.
pub fn dispatcher_loop(
cmd_rx: Receiver<TimedCommand>,
@@ -54,6 +58,13 @@ pub fn dispatcher_loop(
midi_tx: Arc<ArcSwap<Sender<MidiCommand>>>,
link: Arc<LinkState>,
) {
let has_rt = set_realtime_priority();
#[cfg(target_os = "linux")]
if !has_rt {
eprintln!("[cagire] Warning: Could not set realtime priority for dispatcher thread.");
}
let mut queue: BinaryHeap<TimedCommand> = BinaryHeap::with_capacity(256);
loop {
@@ -81,9 +92,9 @@ pub fn dispatcher_loop(
// Dispatch ready commands
let current_us = link.clock_micros() as SyncTime;
while let Some(cmd) = queue.peek() {
if cmd.target_time_us <= current_us + ACTIVE_WAIT_THRESHOLD_US {
if cmd.target_time_us <= current_us + SPIN_THRESHOLD_US {
let cmd = queue.pop().unwrap();
wait_until_dispatch(cmd.target_time_us, &link);
wait_until_dispatch(cmd.target_time_us, &link, has_rt);
dispatch_command(cmd.command, &audio_tx, &midi_tx);
} else {
break;
@@ -92,10 +103,41 @@ pub fn dispatcher_loop(
}
}
/// Active-wait until the target time for precise dispatch.
fn wait_until_dispatch(target_us: SyncTime, link: &LinkState) {
while (link.clock_micros() as SyncTime) < target_us {
std::hint::spin_loop();
/// High-precision sleep using clock_nanosleep on Linux
#[cfg(target_os = "linux")]
fn precise_sleep(micros: u64) {
let duration_ns = micros * 1000;
let ts = libc::timespec {
tv_sec: (duration_ns / 1_000_000_000) as i64,
tv_nsec: (duration_ns % 1_000_000_000) as i64,
};
unsafe {
libc::clock_nanosleep(libc::CLOCK_MONOTONIC, 0, &ts, std::ptr::null_mut());
}
}
#[cfg(not(target_os = "linux"))]
fn precise_sleep(micros: u64) {
thread::sleep(Duration::from_micros(micros));
}
/// Wait until the target time for dispatch.
/// With RT priority: spin-wait for precision
/// Without RT priority: sleep (spinning wastes CPU without benefit)
fn wait_until_dispatch(target_us: SyncTime, link: &LinkState, has_rt: bool) {
let current = link.clock_micros() as SyncTime;
let remaining = target_us.saturating_sub(current);
if has_rt {
// With RT priority: spin-wait for precise timing
while (link.clock_micros() as SyncTime) < target_us {
std::hint::spin_loop();
}
} else {
// Without RT priority: sleep (spin-waiting is counterproductive)
if remaining > 0 {
precise_sleep(remaining);
}
}
}