use jack_sys as j;
use std::fmt;
use std::fmt::Debug;
use std::mem;
use std::sync::atomic::AtomicBool;
use super::callbacks::clear_callbacks;
use super::callbacks::{CallbackContext, NotificationHandler, ProcessHandler};
use crate::client::client_impl::Client;
use crate::client::common::CREATE_OR_DESTROY_CLIENT_MUTEX;
use crate::Error;
#[must_use = "The jack client is shut down when the AsyncClient is dropped. You most likely want to keep this alive and manually tear down with `AsyncClient::deactivate`."]
pub struct AsyncClient<N, P> {
callback: Option<Box<CallbackContext<N, P>>>,
}
unsafe impl<N, P> Send for AsyncClient<N, P> {}
unsafe impl<N, P> Sync for AsyncClient<N, P> {}
impl<N, P> AsyncClient<N, P>
where
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + ProcessHandler,
{
pub fn new(client: Client, notification_handler: N, process_handler: P) -> Result<Self, Error> {
let _m = CREATE_OR_DESTROY_CLIENT_MUTEX.lock().ok();
unsafe {
let mut callback_context = Box::new(CallbackContext {
client,
notification: notification_handler,
process: process_handler,
is_valid_for_callback: AtomicBool::new(true),
has_panic: AtomicBool::new(false),
});
CallbackContext::register_callbacks(&mut callback_context)?;
let res = j::jack_activate(callback_context.client.raw());
match res {
0 => Ok(AsyncClient {
callback: Some(callback_context),
}),
_ => {
mem::forget(callback_context);
Err(Error::ClientActivationError)
}
}
}
}
}
impl<N, P> AsyncClient<N, P> {
#[inline(always)]
pub fn as_client(&self) -> &Client {
let callback = self.callback.as_ref().unwrap();
&callback.client
}
pub fn deactivate(self) -> Result<(Client, N, P), Error> {
let mut c = self;
c.maybe_deactivate()
.map(|c| (c.client, c.notification, c.process))
}
fn maybe_deactivate(&mut self) -> Result<Box<CallbackContext<N, P>>, Error> {
let m = CREATE_OR_DESTROY_CLIENT_MUTEX.lock();
if self.callback.is_none() {
drop(m);
return Err(Error::ClientIsNoLongerAlive);
}
let cb = self.callback.take().ok_or(Error::ClientIsNoLongerAlive)?;
if unsafe { j::jack_deactivate(cb.client.raw()) } != 0 {
drop(m);
return Err(Error::ClientDeactivationError);
}
unsafe { clear_callbacks(cb.client.raw()) }?;
if cb.has_panic.load(std::sync::atomic::Ordering::Relaxed) {
drop(m);
return Err(Error::ClientPanicked);
}
Ok(cb)
}
}
impl<N, P> Drop for AsyncClient<N, P> {
fn drop(&mut self) {
let _ = self.maybe_deactivate();
}
}
impl<N, P> Debug for AsyncClient<N, P> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.debug_tuple("AsyncClient")
.field(&self.as_client())
.finish()
}
}
impl<N, P> AsRef<Client> for AsyncClient<N, P> {
fn as_ref(&self) -> &Client {
self.as_client()
}
}