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.