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