Fixes for *BSDs

Replace linux-only eventfd() with pipe() to enable use on *BSDs for alsa.
Add FreeBSD to supported OSes.
This commit is contained in:
Johannes Lundberg 2017-07-13 13:58:01 +02:00
parent e1380e0d57
commit e86ffce712
2 changed files with 55 additions and 32 deletions

View File

@ -26,6 +26,48 @@ pub type SupportedFormatsIterator = VecIntoIter<Format>;
mod enumerate; mod enumerate;
struct Trigger {
// [read fd, write fd]
fds: [libc::c_int; 2],
}
impl Trigger {
fn new() -> Self {
let mut fds = [0,0];
match unsafe { libc::pipe(fds.as_mut_ptr()) } {
0 => Trigger { fds: fds },
_ => panic!("Could not create pipe")
}
}
fn read_fd(&self) -> libc::c_int {
self.fds[0]
}
fn write_fd(&self) -> libc::c_int {
self.fds[1]
}
fn wakeup(&self) {
let buf = 1u64;
let ret = unsafe { libc::write(self.write_fd(), &buf as *const u64 as *const _, 8) };
assert!(ret == 8);
}
fn clear_pipe(&self) {
let mut out = 0u64;
let ret = unsafe { libc::read(self.read_fd(), &mut out as *mut u64 as *mut _, 8) };
assert_eq!(ret, 8);
}
}
impl Drop for Trigger {
fn drop(&mut self) {
unsafe {
libc::close(self.fds[0]);
libc::close(self.fds[1]);
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct Endpoint(String); pub struct Endpoint(String);
@ -200,11 +242,11 @@ struct EventLoopInner {
// function can pause and add the content of `pending_wait` to `current_wait`. // function can pause and add the content of `pending_wait` to `current_wait`.
pending_wait: Mutex<PollDescriptors>, pending_wait: Mutex<PollDescriptors>,
// A file descriptor opened with `eventfd`. Always the first element // A trigger that uses a `pipe` as backend. Always the first element
// of `current_wait.descriptors`. Should be notified when an element is added // of `current_wait.descriptors`. Should be notified when an element is added
// to `pending_wait` so that the current wait can stop and take the pending wait into // to `pending_wait` so that the current wait can stop and take the pending wait into
// account. // account.
pending_wait_signal: libc::c_int, pending_trigger: Trigger,
} }
struct PollDescriptors { struct PollDescriptors {
@ -217,24 +259,16 @@ struct PollDescriptors {
unsafe impl Send for EventLoopInner {} unsafe impl Send for EventLoopInner {}
unsafe impl Sync for EventLoopInner {} unsafe impl Sync for EventLoopInner {}
impl Drop for EventLoopInner {
fn drop(&mut self) {
unsafe {
libc::close(self.pending_wait_signal);
}
}
}
impl EventLoop { impl EventLoop {
#[inline] #[inline]
pub fn new() -> EventLoop { pub fn new() -> EventLoop {
let pending_wait_signal = unsafe { libc::eventfd(0, 0) }; let pending_trigger = Trigger::new();
EventLoop { EventLoop {
inner: Arc::new(EventLoopInner { inner: Arc::new(EventLoopInner {
current_wait: Mutex::new(PollDescriptors { current_wait: Mutex::new(PollDescriptors {
descriptors: vec![libc::pollfd { descriptors: vec![libc::pollfd {
fd: pending_wait_signal, fd: pending_trigger.read_fd(),
events: libc::POLLIN, events: libc::POLLIN,
revents: 0, revents: 0,
}], }],
@ -244,7 +278,7 @@ impl EventLoop {
descriptors: Vec::new(), descriptors: Vec::new(),
voices: Vec::new(), voices: Vec::new(),
}), }),
pending_wait_signal: pending_wait_signal, pending_trigger: pending_trigger,
}) })
} }
} }
@ -274,10 +308,7 @@ impl EventLoop {
current_wait.voices.append(&mut pending.voices); current_wait.voices.append(&mut pending.voices);
// Emptying the signal. // Emptying the signal.
let mut out = 0u64; self.inner.pending_trigger.clear_pipe();
let ret = libc::read(self.inner.pending_wait_signal,
&mut out as *mut u64 as *mut _, 8);
assert_eq!(ret, 8);
} }
// Check each individual descriptor for events. // Check each individual descriptor for events.
@ -402,7 +433,7 @@ struct VoiceInner {
// A file descriptor opened with `eventfd`. // A file descriptor opened with `eventfd`.
// It is used to wait for resume signal. // It is used to wait for resume signal.
resume_signal: libc::c_int, resume_trigger: Trigger,
} }
unsafe impl Send for VoiceInner {} unsafe impl Send for VoiceInner {}
@ -439,7 +470,7 @@ impl SamplesStream {
// And we add the descriptor corresponding to the resume signal // And we add the descriptor corresponding to the resume signal
// to `event_loop.pending_wait.descriptors`. // to `event_loop.pending_wait.descriptors`.
pending_wait.descriptors.push(libc::pollfd { pending_wait.descriptors.push(libc::pollfd {
fd: self.inner.resume_signal, fd: self.inner.resume_trigger.read_fd(),
events: libc::POLLIN, events: libc::POLLIN,
revents: 0, revents: 0,
}); });
@ -452,10 +483,7 @@ impl SamplesStream {
// Now that `pending_wait` received additional descriptors, we signal the event // Now that `pending_wait` received additional descriptors, we signal the event
// so that our event loops can pick it up. // so that our event loops can pick it up.
drop(pending_wait); drop(pending_wait);
let buf = 1u64; self.inner.event_loop.pending_trigger.wakeup();
let wret = libc::write(self.inner.event_loop.pending_wait_signal,
&buf as *const u64 as *const _, 8);
assert!(wret == 8);
} }
} }
} }
@ -611,7 +639,7 @@ impl Voice {
period_len: period_len, period_len: period_len,
scheduled: Mutex::new(None), scheduled: Mutex::new(None),
is_paused: AtomicBool::new(true), is_paused: AtomicBool::new(true),
resume_signal: libc::eventfd(0, 0), resume_trigger: Trigger::new(),
}); });
Ok((Voice { Ok((Voice {
@ -627,12 +655,7 @@ impl Voice {
// If it was paused then we resume and signal // If it was paused then we resume and signal
// FIXME: the signal is send even if the event loop wasn't waiting for resume, is that an issue ? // FIXME: the signal is send even if the event loop wasn't waiting for resume, is that an issue ?
if self.inner.is_paused.swap(false, Ordering::Relaxed) { if self.inner.is_paused.swap(false, Ordering::Relaxed) {
unsafe { self.inner.resume_trigger.wakeup();
let buf = 1u64;
let wret = libc::write(self.inner.resume_signal,
&buf as *const u64 as *const _, 8);
assert!(wret == 8);
}
} }
} }

View File

@ -76,7 +76,7 @@ extern crate libc;
pub use samples_formats::{SampleFormat, Sample}; pub use samples_formats::{SampleFormat, Sample};
#[cfg(all(not(windows), not(target_os = "linux"), not(target_os = "macos")))] #[cfg(all(not(windows), not(target_os = "linux"), not(target_os = "freebsd"), not(target_os = "macos")))]
use null as cpal_impl; use null as cpal_impl;
use std::fmt; use std::fmt;
@ -89,7 +89,7 @@ use futures::Poll;
mod null; mod null;
mod samples_formats; mod samples_formats;
#[cfg(target_os = "linux")] #[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[path="alsa/mod.rs"] #[path="alsa/mod.rs"]
mod cpal_impl; mod cpal_impl;