0

I'm writing Rust wrappers for C bindings so that they look more Rusty. One such C function is this:

void mosquitto_connect_callback_set(
    struct mosquitto * mosq,
    void (*on_connect)(struct mosquitto *, void *, int)
)

I'm using the below technique to pass a Rust closure as the user data to above binding (void* in the callback) so that the Rust closure will be called when the C callback is invoked.

//  Registered callback is called when the broker sends a CONNACK message in response
// to a connection. Will be called even incase of failure. All your sub/pub stuff
// should ideally be done in this callback when connection is successful

pub fn onconnect_callback<F>(&self, callback: F)
    where F: Fn(i32)
{
    // Convert the rust closure into void* to be used as user_data. This will
    // be passed to callback automatically by the library
    let cb = &callback as *const _ as *mut libc::c_void;

    unsafe {
        // Set our closure as user data
        bindings::mosquitto_user_data_set(self.mosquitto, cb);
        // Register callback
        bindings::mosquitto_connect_callback_set(self.mosquitto, Some(onconnect_wrapper::<F>));
    }

    // Registered callback. user data is our closure
    unsafe extern "C" fn onconnect_wrapper<F>(mqtt: *mut bindings::Struct_mosquitto,
                                              closure: *mut libc::c_void,
                                              val: libc::c_int)
        where F: Fn(i32)
    {
        let closure = closure as *mut F;
        println!("rc = {:?}", val as i32);
        (*closure)(val as i32);

    }
}

But the problem is that user data is set using a function instead of directly passing it to the callback set function

// Set our closure as user data
bindings::mosquitto_user_data_set(self.mosquitto, cb);

I think the callback: F closure passed to onconnect_callback might get destroyed by the time the actual C callback is invoked. This might be the reason I'm getting garbage values when capturing a variable.

let i = 100;

client.onconnect_callback(|a: i32|{
    println!("i = {:?}", i);
    println!("@@@ On connect callback {}@@@", a)
    });

match client.connect("localhost"){
    Ok(_) => println!("Connection successful --> {:?}", client),
    Err(n) => panic!("Connection error = {:?}", n)
}

OUTPUT:

i = 734146560
@@@ On connect callback 0@@@

How do I fix this without passing closure as reference?

The full code

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
tez
  • 4,990
  • 12
  • 47
  • 67
  • 2
    Your implementation is broken and produces insecure code that reads from undefined memory. Your `onconnect_callback ` method **takes ownership** of the `callback` value, and thus the reference to it is only valid while that method lives. Rust would point that out for you, except you convert it into a raw pointer, which means you take responsibility for lifetimes. Reread the [answer that suggested this technique](http://stackoverflow.com/a/32270215/155423), especially the part "*this would only work if the [function] does not store the pointer to callback*". – Shepmaster Dec 13 '15 at 04:31
  • 2
    I'd say that the variant with `Box` which I've added in that answer should solve your problem. Please consider it again. If it does work for you, we'll close this question as a duplicate; if it doesn't, please edit your answer to explain what exactly does not work with the `Box` approach suggested there, and we'll try to find a solution then. – Vladimir Matveev Dec 13 '15 at 21:28
  • Sure. I'll try that today. – tez Dec 14 '15 at 03:13
  • @VladimirMatveev Thank you very much. The method you suggested works. But this is not an option in my case because other callbacks are overwriting the user data and wrong closures are getting invoked. https://users.rust-lang.org/t/garbage-value-in-callback-closure/3942/5 . I used an alternate way (in the link). If it is ok, can you please take a look at that. – tez Dec 16 '15 at 07:39
  • @tez yes, I think your approach is fine, given the constraints of your C library API (I wonder why it allows only one piece of user data for all callbacks...). – Vladimir Matveev Dec 16 '15 at 13:48

0 Answers0