Skip to content

Commit

Permalink
Merge pull request #502 from orottier/bugfix/all-futures-send-sync-st…
Browse files Browse the repository at this point in the history
…atic

Ensure all async fns return a Send + Sync Future object
  • Loading branch information
orottier authored Apr 22, 2024
2 parents 5c25551 + 520eaf0 commit 99bef60
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 18 deletions.
11 changes: 11 additions & 0 deletions src/context/offline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,4 +630,15 @@ mod tests {

assert!(complete.load(Ordering::Relaxed));
}

fn require_send_sync<T: Send + Sync>(_: T) {}

#[test]
fn test_all_futures_thread_safe() {
let context = OfflineAudioContext::new(2, 555, 44_100.);

require_send_sync(context.start_rendering());
require_send_sync(context.suspend(1.));
require_send_sync(context.resume());
}
}
52 changes: 34 additions & 18 deletions src/context/online.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,29 +470,30 @@ impl AudioContext {
///
/// * The audio device is not available
/// * For a `BackendSpecificError`
#[allow(clippy::await_holding_lock)] // false positive due to explicit drop
pub async fn resume(&self) {
// Lock the backend manager mutex to avoid concurrent calls
log::debug!("Resume called, locking backend manager");
let backend_manager_guard = self.backend_manager.lock().unwrap();
let (sender, receiver) = oneshot::channel();

if self.state() != AudioContextState::Suspended {
log::debug!("Resume no-op - context is not suspended");
return;
}
{
// Lock the backend manager mutex to avoid concurrent calls
log::debug!("Resume called, locking backend manager");
let backend_manager_guard = self.backend_manager.lock().unwrap();

// Ask the audio host to resume the stream
backend_manager_guard.resume();
if self.state() != AudioContextState::Suspended {
log::debug!("Resume no-op - context is not suspended");
return;
}

// Then, ask to resume rendering via a control message
log::debug!("Resumed audio stream, waking audio graph");
let (sender, receiver) = oneshot::channel();
let notify = OneshotNotify::Async(sender);
self.base
.send_control_msg(ControlMessage::Resume { notify });
// Ask the audio host to resume the stream
backend_manager_guard.resume();

// Drop the Mutex guard so we won't hold it across an await
drop(backend_manager_guard);
// Then, ask to resume rendering via a control message
log::debug!("Resumed audio stream, waking audio graph");
let notify = OneshotNotify::Async(sender);
self.base
.send_control_msg(ControlMessage::Resume { notify });

// Drop the Mutex guard so we won't hold it across an await point
}

// Wait for the render thread to have processed the resume message
// The AudioContextState will be updated by the render thread.
Expand Down Expand Up @@ -767,4 +768,19 @@ mod tests {
let time5 = context.current_time();
assert_eq!(time5, time4); // no progression of time
}

fn require_send_sync<T: Send + Sync>(_: T) {}

#[test]
fn test_all_futures_thread_safe() {
let options = AudioContextOptions {
sink_id: "none".into(),
..AudioContextOptions::default()
};
let context = AudioContext::new(options);

require_send_sync(context.suspend());
require_send_sync(context.resume());
require_send_sync(context.close());
}
}

0 comments on commit 99bef60

Please sign in to comment.