EXC_BAD_ACCESS using c_void from a thread

I am writing a small library in Rust, initially for use from a C++ app.
I am storing callbacks (function pointer + userdata context pointer as c_void), which are set from the C++ app and called from the Rust lib. In this case, userdata refers to a C++ object.

I can call the callback fine from the main thread. However, the library runs a timer on a thread, which is where the callbacks need to be called from. Once I moved the callback call into the timer, I got an exception because the *mut c_void pointer has become null. I can clearly see now, calling the callback outside the thread is fine, inside the thread, it crashes.

The crash happens specifically, if I try and access this within the C++ callback.

Below is simplified code from the library.

#[no_mangle]  
pub type StatsCallback = extern "C" fn(stats: Statistics, caller: *mut c_void);

#[repr(C)]
pub struct StatsCallbackStore {
    pub callback: StatsCallback,
    userdata: *mut c_void,
}

impl StatsCallbackStore {
    pub fn call(&self, stats: Statistics) {
        (self.callback)(stats, self.userdata)
    }
}

pub struct SerialConnection
{
    pub stats_callback_store: Option<StatsCallbackStore>,
}

impl SerialConnection
{
    //...
    pub fn start_receiving(&mut self)
    {
        //...
        let (timer_tx, timer_rx) = channel();

        // Spawn one second timer
        thread::spawn(move || {
            loop {
                thread::sleep(Duration::from_secs(1));
                timer_tx.send(true).unwrap();
            }
        });

        loop {
            // Grab some data from a serial port
            let _ = timer_rx.try_recv().map(|reply| {
                if reply {
                    println!("reply!");
                    // Process data and post to callback

                    match self.stats_callback_store.as_ref() {
                        Some(callback_store) => {
                            // EXC_BAD_ACCESS in here if it tries to 
                            // access `userdata` (`this` on the C++ side)
                            callback_store.call(stats); 
                        }
                        None => {}
                    }

                    line_count = 0;
                }
            });
        }
    }
}


I have started constructing a simplified example but even that is a little complex due to the C++/Rust interop. So I'm asking here in case anyone has a clue, and if it makes sense I will continue and post a simple example project.

OK, this was a problem on the C++ side - the callback was being created on the stack and deallocated which is why it became NULL!! I was writing the code as if the Rust side would take ownership of the callback...

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.