Hello I have a callback based C interface, and now I want to test it in rust, for example
extern "C" fn read_data( callback: extern "C" fn(*mut std::ffi::c_void, bool),
callback_data: *mut std::ffi::c_void){
struct Sendable{
callback: extern "C" fn(*mut std::ffi::c_void, bool),
callback_data: *mut std::ffi::c_void
};
unsafe impl Send for Sendable{};
let sendable = Sendable{
callback,
callback_data
};
let _detached = std::thread::spawn(move||{
let read_ok = true;
(sendable.callback)(sendable.callback_data, read_ok);
});
}
fn test_read_data(){
let mut read_result = false;
let (tx, mut rx) = tokio::sync::oneshot::channel::<bool>();
let mut callback_data = (&mut read_result as *mut bool,
tx);
extern "C" fn test_callback(callback_data: *mut std::ffi::c_void, result :bool){
let cast_back = callback_data as *mut (*mut bool, tokio::sync::oneshot::Sender::<bool>);
unsafe{*(*cast_back).0 = result;}
unsafe{(*cast_back)}.1.send(true); // cannot move out of *cast_back which is behind a raw pointer move occurs because ....
//right here does rust test_callback already takes ownership of dereferenced tuple
// so I have to make rust 'leak' it (the tuple still owned in read_data scope) to avoid double free?
}
read_data(test_callback,
&mut callback_data as *mut (*mut bool,
tokio::sync::oneshot::Sender::<bool>) as *mut _ );
let wait = rx.try_recv();
assert!(unsafe{*callback_data.0});
}
read_data is exported as C interface, so I have to cast pointer instead of other idiomatic rust ways. now I have 3 objects :
-
the passed in callback_data(behind pointer), I need it lifetime binded to test_read_data, the callback should only using its reference. (does dereference pointer means 'dereferenced value lifetime taken by rust' ? )
-
the bool field of callback_data, I also need it lifetime binded to test_read_data, the callback should only set it value
-
the tokio Sender object, I want it 'moved' from test_read_data into 'callback', since the send method need to move Sender object.
I can't find a correct way to satisfy different ownership/lifetime, my question is
-
what's the idiomatic rust way to do such test ? (read_data signature can't be changed, must be c callback and void* based, and test entry/ test_callback must be also in rust)
-
ignore the none-idiomatic (or maybe thread bugs) of my code, how to do such thing in rust? I mean, object lifetime still bind to current scope, but passing its pointer out, and at other place(through pointer dereference) , only parts of object member is moved, and then current scope still able to access the none-moved member ? I'm thinking using a Box to wrap the member, but also not success.
any suggestions will be greatly appreciated