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

Cannot borrow a variable in the jack process callback #60

Closed
PieterPenninckx opened this issue Feb 17, 2017 · 6 comments
Closed

Cannot borrow a variable in the jack process callback #60

PieterPenninckx opened this issue Feb 17, 2017 · 6 comments

Comments

@PieterPenninckx
Copy link

I want to use a variable that I borrow from elsewhere in the process callback.
First method is to use the closure approach (example is a slightly modification of the playback_capture example):

extern crate jack;
use jack::prelude as j;
use std::io;

fn handle_audio(i: &u64) {
    let (client, _status) = j::Client::new("rust_jack_simple", j::client_options::NO_START_SERVER)
        .unwrap();

    let in_a = client.register_port("rust_in_l", j::AudioInSpec::default()).unwrap();
    let mut out_a = client.register_port("rust_out_l", j::AudioOutSpec::default()).unwrap();
    let process_callback = move |_: &j::Client, ps: &j::ProcessScope| -> j::JackControl {
        let mut out_a_p = j::AudioOutPort::new(&mut out_a, ps);
        let in_a_p = j::AudioInPort::new(&in_a, ps);
        out_a_p.clone_from_slice(&in_a_p);
        let k = *i; // Using the reference in the callback is not possible.
        j::JackControl::Continue
    };
    let process = j::ProcessHandler::new(process_callback);
    let active_client = j::AsyncClient::new(client, process).unwrap();
    
    /* ... */

    active_client.deactivate().unwrap();
}

fn main() {
	let i : u64 = 0;
	handle_audio(&i);
}

When I try this, I get the following example:

error[E0477]: the type `[[email protected]:11:28: 17:6 out_a:jack::port::Port<jack::port::AudioOutSpec>, in_a:jack::port::Port<jack::port::AudioInSpec>, i:&u64]` does not fulfill the required lifetime

Second method is to use a struct that implements the JackHandler trait and that borrows the variable (again a slightly modified version of the playback_capture example):

extern crate jack;
use jack::prelude as j;
use std::io;

struct Handler<'a> {
	i: &'a u64, // Borrow a variable in the struct
	out_a: j::Port<j::AudioOutSpec>,
	in_a: j::Port<j::AudioInSpec>,
}

impl<'a> j::JackHandler for Handler<'a> {
	fn process(&self, _: &j::Client, ps: &j::ProcessScope) -> j::JackControl {
		let k = *self.i; // Use the borrowed variable
        let mut out_a_p = j::AudioOutPort::new(&mut self.out_a, ps); // Error: cannot borrow self as mutable
        let in_a_p = j::AudioInPort::new(&self.in_a, ps);
        out_a_p.clone_from_slice(&in_a_p);
        j::JackControl::Continue
	}
}

fn handle_audio(i: &u64) {
    let (client, _status) = j::Client::new("rust_jack_simple", j::client_options::NO_START_SERVER)
        .unwrap();

    let in_a = client.register_port("rust_in_l", j::AudioInSpec::default()).unwrap();
    let mut out_a = client.register_port("rust_out_l", j::AudioOutSpec::default()).unwrap();
    let handler = Handler{i: i, out_a: out_a, in_a: in_a};
    let active_client = j::AsyncClient::new(client, handler).unwrap();

    active_client.deactivate().unwrap();
}

fn main() {
	let i : u64 = 0;
	handle_audio(&i);
}

Here, I get the following error:

error: cannot borrow immutable field `self.out_a` as mutable

I could get around this second error by using interior mutability with a Mutex (RefCell doesn't work because it doesn't implement Sync), but the documentation of rust-jack especially discourages calling functions like pthread_mutex_lock in the process function, so this doesn't seem to be a good idea. For implementing the JackHandler trait for certain closures, an unsafe mutable transmute is used ( rust-jack/src/client/callbacks.rs, line 149), so that's still an option.

Am I missing something else? Is it a good idea to have the process function in the JackHandler take an &mut self parameter instead of &self?

@wmedrano
Copy link
Member

Taking &mut should be safe as long as no callbacks run at the same time, which I haven't verified or found docs about. I'll post in JACK this weekend to verify.

On a similar note, I was planning on having a handler that implements the ProcessHandler trait by taking closures. IE:

let my_handler = ClosureHandler::new()
    .with_process(|....|....)
    .with_port_rename(|...|....);

@PieterPenninckx
Copy link
Author

Thanks.

@wmedrano
Copy link
Member

Posted question here, but from docs, it looks like I could make &mut if I move the fn process over to a new object.

@wmedrano
Copy link
Member

You can try out this branch https://github.com/wmedrano/rust-jack/tree/mut-handlers

You can either implement the NotificationHandler and ProcessHandler traits directly (see src/client/callbacks.rs for trait definitions), or use the closure based versions like in examples/playback_capture.rs.

@PieterPenninckx
Copy link
Author

Thanks for the very quick response. I hope to find some time next week-end to try it out.

I had a quick look at the definition of the ProcessHandler trait and it seems nice. The closure based version like in the example is very ergonomic but has a lifetime requirement that I cannot satisfy (which was the original reason why I was trying to implement the JackHandler trait).

@PieterPenninckx
Copy link
Author

The pull request solved it for me. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants