Fix state transition synchronisation in ASIO

This makes some tweaks to the ASIO backend in order to fix some cases
where races may have occured. This should allow us to remove the `Sync`
bound on the `Device` and `Host` types.
This commit is contained in:
mitchmindtree 2020-01-12 19:30:04 +01:00
parent 33ddf74954
commit ca2aceb536
1 changed files with 53 additions and 36 deletions

View File

@ -7,7 +7,7 @@ use self::errors::{AsioError, AsioErrorWrapper, LoadDriverError};
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::{c_char, c_double, c_long, c_void};
use std::sync::{Arc, Mutex, Weak};
use std::sync::{Arc, Mutex, MutexGuard, Weak};
// Bindings import
use self::asio_import as ai;
@ -85,7 +85,7 @@ pub struct SampleRate {
}
/// Holds the pointer to the callbacks that come from cpal
struct BufferCallback(Box<FnMut(i32) + Send>);
struct BufferCallback(Box<dyn FnMut(i32) + Send>);
/// Input and Output streams.
///
@ -422,6 +422,8 @@ impl Driver {
// To pass as ai::ASIOCallbacks
let mut callbacks = create_asio_callbacks();
let mut state = self.inner.lock_state();
// Retrieve the available buffer sizes.
let buffer_sizes = asio_get_buffer_sizes()?;
if buffer_sizes.pref <= 0 {
@ -432,13 +434,12 @@ impl Driver {
}
// Ensure the driver is in the `Initialized` state.
if let DriverState::Running = self.inner.state() {
self.stop()?;
if let DriverState::Running = *state {
state.stop()?;
}
if let DriverState::Prepared = self.inner.state() {
self.dispose_buffers()?;
if let DriverState::Prepared = *state {
state.dispose_buffers()?;
}
unsafe {
asio_result!(ai::ASIOCreateBuffers(
buffer_infos.as_mut_ptr() as *mut _,
@ -447,8 +448,8 @@ impl Driver {
&mut callbacks as *mut _ as *mut _,
))?;
}
*state = DriverState::Prepared;
self.inner.set_state(DriverState::Prepared);
Ok(buffer_sizes.pref)
}
@ -569,13 +570,14 @@ impl Driver {
///
/// No-op if already `Running`.
pub fn start(&self) -> Result<(), AsioError> {
if let DriverState::Running = self.inner.state() {
let mut state = self.inner.lock_state();
if let DriverState::Running = *state {
return Ok(());
}
unsafe {
asio_result!(ai::ASIOStart())?;
}
self.inner.set_state(DriverState::Running);
*state = DriverState::Running;
Ok(())
}
@ -635,55 +637,70 @@ impl Driver {
}
}
impl DriverInner {
fn state(&self) -> DriverState {
*self.state.lock().expect("failed to lock `DriverState`")
}
fn set_state(&self, state: DriverState) {
*self.state.lock().expect("failed to lock `DriverState`") = state;
}
fn stop_inner(&self) -> Result<(), AsioError> {
if let DriverState::Running = self.state() {
impl DriverState {
fn stop(&mut self) -> Result<(), AsioError> {
if let DriverState::Running = *self {
unsafe {
asio_result!(ai::ASIOStop())?;
}
self.set_state(DriverState::Prepared);
*self = DriverState::Prepared;
}
Ok(())
}
fn dispose_buffers_inner(&self) -> Result<(), AsioError> {
if let DriverState::Initialized = self.state() {
fn dispose_buffers(&mut self) -> Result<(), AsioError> {
if let DriverState::Initialized = *self {
return Ok(());
}
if let DriverState::Running = self.state() {
self.stop_inner()?;
if let DriverState::Running = *self {
self.stop()?;
}
unsafe {
asio_result!(ai::ASIODisposeBuffers())?;
}
self.set_state(DriverState::Initialized);
*self = DriverState::Initialized;
Ok(())
}
fn destroy_inner(&mut self) -> Result<(), AsioError> {
// Drop back through the driver state machine one state at a time.
if let DriverState::Running = self.state() {
self.stop_inner()?;
fn destroy(&mut self) -> Result<(), AsioError> {
if let DriverState::Running = *self {
self.stop()?;
}
if let DriverState::Prepared = self.state() {
self.dispose_buffers_inner()?;
if let DriverState::Prepared = *self {
self.dispose_buffers()?;
}
unsafe {
asio_result!(ai::ASIOExit())?;
ai::remove_current_driver();
}
Ok(())
}
}
// Clear any existing stream callbacks.
if let Ok(mut bcs) = BUFFER_CALLBACK.lock() {
bcs.clear();
impl DriverInner {
fn lock_state(&self) -> MutexGuard<DriverState> {
self.state.lock().expect("failed to lock `DriverState`")
}
fn stop_inner(&self) -> Result<(), AsioError> {
let mut state = self.lock_state();
state.stop()
}
fn dispose_buffers_inner(&self) -> Result<(), AsioError> {
let mut state = self.lock_state();
state.dispose_buffers()
}
fn destroy_inner(&mut self) -> Result<(), AsioError> {
{
let mut state = self.lock_state();
state.destroy()?;
// Clear any existing stream callbacks.
if let Ok(mut bcs) = BUFFER_CALLBACK.lock() {
bcs.clear();
}
}
// Signal that the driver has been destroyed.