Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sync requirement to handlers. #110

Merged
merged 4 commits into from
Sep 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ bitflags = "1.1"
jack-sys = { path = "./jack-sys", version = "0.2" }
lazy_static = "1.4"
libc = "0.2"

[dev-dependencies]
crossbeam-channel = "0.3"
27 changes: 8 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,35 +49,24 @@ Missing categories include, JACK threading, synchronous processing, transport an

## Testing

Testing is a little awkward to setup since it relies on a JACK server.

### Setting Up JACK Dummy Server
Testing requires setting up a dummy server and running the tests using a single
thread.

```bash
$ # Set up a dummy server for tests.
$ ./dummy_jack_server.sh
$ # Run tests with limited concurrency.
$ RUST_TEST_THREADS=1 cargo test
```

which runs the command

```bash
$ jackd -r -ddummy -r44100 -p1024 & # Start the dummy JACK server
```

Testing expects there to be an available JACK server running at a sample rate of
44.1kHz and a buffer size of 1024 samples.

### Running the tests

```bash
$ cargo test
```
**Note:** We use a single thread for tests since too multiple client
instantiations in short periods of time cause the JACK server to become flaky.

#### Possible Issues

If the tests are failing, a possible gotcha may be timing issues.

1. Rust runs tests in parallel, it may be possible that the JACK server is not keeping up. Set the environment variable `RUST_TEST_THREADS` to 1.
2. Increase the value used by `sleep_on_test` in `client/common.rs`.
1. Increase the value used by `sleep_on_test` in `client/common.rs`.

Another case is that libjack may be broken on your setup. Try switching between
libjack and libjack2 (they have the same API and libjack2 isn't necessarily
Expand Down
6 changes: 4 additions & 2 deletions examples/sine.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Sine wave generator with frequency configuration exposed through standard
//! input.
extern crate crossbeam_channel;
extern crate jack;

use crossbeam_channel::bounded;
use std::io;
use std::str::FromStr;
use std::sync::mpsc::channel;

fn main() {
// 1. open a client
Expand All @@ -20,7 +22,7 @@ fn main() {
let sample_rate = client.sample_rate();
let frame_t = 1.0 / sample_rate as f64;
let mut time = 0.0;
let (tx, rx) = channel();
let (tx, rx) = bounded(1_000_000);
let process = jack::ClosureProcessHandler::new(
move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
// Get output buffer
Expand Down
4 changes: 2 additions & 2 deletions src/client/async_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ pub struct AsyncClient<N, P> {

impl<N, P> AsyncClient<N, P>
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
/// Tell the JACK server that the program is ready to start processing audio. JACK will call the
/// methods specified by the `NotificationHandler` and `ProcessHandler` objects.
Expand Down
56 changes: 28 additions & 28 deletions src/client/callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ pub trait ProcessHandler: Send {

unsafe extern "C" fn thread_init_callback<N, P>(data: *mut libc::c_void)
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.thread_init(&ctx.client)
Expand All @@ -174,8 +174,8 @@ unsafe extern "C" fn shutdown<N, P>(
reason: *const libc::c_char,
data: *mut libc::c_void,
) where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let cstr = ffi::CStr::from_ptr(reason);
Expand All @@ -191,8 +191,8 @@ unsafe extern "C" fn shutdown<N, P>(

unsafe extern "C" fn process<N, P>(n_frames: Frames, data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let scope = ProcessScope::from_raw(n_frames, ctx.client.raw());
Expand All @@ -201,8 +201,8 @@ where

unsafe extern "C" fn freewheel<N, P>(starting: libc::c_int, data: *mut libc::c_void)
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let is_starting = match starting {
Expand All @@ -214,17 +214,17 @@ where

unsafe extern "C" fn buffer_size<N, P>(n_frames: Frames, data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.buffer_size(&ctx.client, n_frames).to_ffi()
}

unsafe extern "C" fn sample_rate<N, P>(n_frames: Frames, data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.sample_rate(&ctx.client, n_frames).to_ffi()
Expand All @@ -235,8 +235,8 @@ unsafe extern "C" fn client_registration<N, P>(
register: libc::c_int,
data: *mut libc::c_void,
) where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let name = ffi::CStr::from_ptr(name).to_str().unwrap();
Expand All @@ -253,8 +253,8 @@ unsafe extern "C" fn port_registration<N, P>(
register: libc::c_int,
data: *mut libc::c_void,
) where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let register = match register {
Expand All @@ -273,8 +273,8 @@ unsafe extern "C" fn port_rename<N, P>(
data: *mut libc::c_void,
) -> libc::c_int
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let old_name = ffi::CStr::from_ptr(old_name).to_str().unwrap();
Expand All @@ -290,8 +290,8 @@ unsafe extern "C" fn port_connect<N, P>(
connect: libc::c_int,
data: *mut libc::c_void,
) where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let are_connected = match connect {
Expand All @@ -304,26 +304,26 @@ unsafe extern "C" fn port_connect<N, P>(

unsafe extern "C" fn graph_order<N, P>(data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.graph_reorder(&ctx.client).to_ffi()
}

unsafe extern "C" fn xrun<N, P>(data: *mut libc::c_void) -> libc::c_int
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
ctx.notification.xrun(&ctx.client).to_ffi()
}

unsafe extern "C" fn latency<N, P>(mode: j::jack_latency_callback_mode_t, data: *mut libc::c_void)
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
let ctx = CallbackContext::<N, P>::from_raw(data);
let mode = match mode {
Expand Down Expand Up @@ -361,8 +361,8 @@ pub struct CallbackContext<N, P> {

impl<N, P> CallbackContext<N, P>
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
pub unsafe fn from_raw<'a>(ptr: *mut libc::c_void) -> &'a mut CallbackContext<N, P> {
debug_assert!(!ptr.is_null());
Expand Down
4 changes: 2 additions & 2 deletions src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ impl Client {
process_handler: P,
) -> Result<AsyncClient<N, P>, Error>
where
N: 'static + Send + NotificationHandler,
P: 'static + Send + ProcessHandler,
N: 'static + Send + Sync + NotificationHandler,
P: 'static + Send + Sync + ProcessHandler,
{
AsyncClient::new(self, notification_handler, process_handler)
}
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ extern crate bitflags;
extern crate jack_sys;
#[macro_use]
extern crate lazy_static;
#[cfg(test)]
extern crate crossbeam_channel;
extern crate libc;

pub use client::{
Expand Down
4 changes: 2 additions & 2 deletions src/port/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl Port<AudioOut> {

#[cfg(test)]
mod test {
use std::sync::mpsc::channel;
use crossbeam_channel::bounded;

use super::*;
use Client;
Expand All @@ -118,7 +118,7 @@ mod test {
let in_b = c.register_port("ib", AudioIn::default()).unwrap();
let mut out_a = c.register_port("oa", AudioOut::default()).unwrap();
let mut out_b = c.register_port("ob", AudioOut::default()).unwrap();
let (signal_succeed, did_succeed) = channel();
let (signal_succeed, did_succeed) = bounded(1_000);
let process_callback = move |_: &Client, ps: &ProcessScope| -> Control {
let exp_a = 0.312_443;
let exp_b = -0.612_120;
Expand Down
4 changes: 2 additions & 2 deletions src/port/midi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@ mod test {
use client::Client;
use client::ClosureProcessHandler;
use client::ProcessHandler;
use crossbeam_channel::bounded;
use jack_enums::Control;
use primitive_types::Frames;
use std::iter::Iterator;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::channel;
use std::sync::Mutex;
use std::{thread, time};
use ClientOptions;
Expand Down Expand Up @@ -327,7 +327,7 @@ mod test {
let mut out_b = c.register_port("ob", MidiOut::default()).unwrap();

// set callback routine
let (signal_succeed, did_succeed) = channel();
let (signal_succeed, did_succeed) = bounded(1_000);
let process_callback = move |_: &Client, ps: &ProcessScope| -> Control {
let exp_a = RawMidi {
time: 0,
Expand Down