Skip to content

Commit 393191d

Browse files
committed
Update guide-ffi.md
Simplified the first examples to demonstrate callbacks without other threads involved and shortened the elaboration about async callbacks.
1 parent 67d83ac commit 393191d

File tree

1 file changed

+60
-98
lines changed

1 file changed

+60
-98
lines changed

doc/guide-ffi.md

+60-98
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,8 @@ fn main() {
251251

252252
# Callbacks from C code to Rust functions
253253

254-
Some external libraries require the usage of callbacks.
255-
E.g. because they start background threads and use callbacks to signal events
256-
like the availability of new data.
254+
Some external libraries require the usage of callbacks to report back their
255+
current state or intermediate data to the caller.
257256
It is possible to pass functions defined in Rust to an external library.
258257
The requirement for this is that the callback function is marked as `extern`
259258
with the correct calling convention to make it callable from C code.
@@ -265,24 +264,25 @@ A basic example is:
265264

266265
Rust code:
267266
~~~~
268-
extern "C" fn callback(a:i32) {
267+
extern fn callback(a:i32) {
269268
println!("I'm called from C with value {0}", a);
270269
}
271270
272271
#[link(name = "extlib")]
273272
extern {
274273
fn register_callback(cb: extern "C" fn(i32)) -> i32;
274+
fn trigger_callback();
275275
}
276276
277277
fn main() {
278278
unsafe {
279279
register_callback(callback);
280+
trigger_callback(); // Triggers the callback
280281
}
281-
... // Do sth. and wait for callbacks
282282
}
283283
~~~~
284284

285-
C-Code:
285+
C code:
286286
~~~~
287287
typedef void (*rust_callback)(int32_t);
288288
rust_callback cb;
@@ -292,15 +292,14 @@ int32_t register_callback(rust_callback callback) {
292292
return 1;
293293
}
294294
295-
void thread() {
296-
// do sth
295+
void trigger_callback() {
297296
cb(7); // Will call callback(7) in Rust
298297
}
299298
~~~~
300299

301-
Keep in mind that `callback()` will be called from a C thread and not from
302-
a Rust thread or even your main thread. Therefore each data access is
303-
especially unsafe and synchronization mechanisms must be used.
300+
In this example will Rust's `main()` will call `do_callback()` in C,
301+
which would call back to `callback()` in Rust.
302+
304303

305304
## Targetting callbacks to Rust objects
306305

@@ -314,115 +313,78 @@ C library. The C library can then include the pointer to the Rust object in
314313
the notification. This will provide a unsafe possibility to access the
315314
referenced Rust object in callback.
316315

317-
If this mechanism is used it is absolutely necessary that no more callbacks
318-
are performed by C library after the respective Rust object get's
319-
destroyed. This can be achieved by unregistering the callback it the object's
320-
destructor and designing the library in a way that guarantees that no
321-
callback will be performed after unregistration.
322-
323-
## Sychronzing callbacks with channels
324-
325-
As already explained accessing data of a Rust object in a callback is unsafe
326-
without synchronisation. Channels in Rust provide a mechanism
327-
which can be used to forward events into Rust tasks. The idea is to create a
328-
channel where the writing end (`Chan`) is used exclusively from the C callback
329-
to queue events. The reading end (`Port`) is used in the Rust task which owns
330-
the wrapper object.
331-
332-
Depending on the type of data in the event you might want to convert callback
333-
data from C into a more suitable Rust structure before sending it into the
334-
channel. E.g. it makes sense to convert C strings (`char*`) into Rust strings.
335-
You could also use Rust enumerations to differentiate between multiple types
336-
of events and their data.
337-
338-
Putting this together a wrapper for a library that uses a background thread
339-
that sends events could look like:
340-
341316
Rust code:
342317
~~~~
343318
344-
#[link(name = "extlib")]
345-
extern {
346-
fn init(target: *ExtLibWrapper, cb: extern "C" fn(*ExtLibWrapper, EventData));
347-
fn unregister();
348-
}
349-
350-
struct EventData {
351-
... // Contains data that describes the event
319+
struct RustObject {
320+
a: i32,
321+
// other members
352322
}
353323
354-
pub struct ExtLibWrapper {
355-
// Channel is used privately
356-
priv chan: comm::Chan<EventData>,
357-
358-
// The port is used by the Rust task to receive notifications
359-
port: comm::Port<EventData>
360-
}
361-
362-
impl ExtLibWrapper {
363-
pub fn new() -> ~ EventData {
364-
let (p,c):(Port<EventData>,Chan<EventData>)
365-
= comm::Chan::new();
366-
367-
let wrapper = ~ExtLibWrapper{chan:c, port:p};
368-
369-
unsafe {
370-
let wrapper_addr:*ExtLibWrapper = ptr::to_unsafe_ptr(wrapper);
371-
init(wrapper_addr, callback);
372-
}
373-
}
324+
extern fn callback(target: *RustObject, a:i32) {
325+
println!("I'm called from C with value {0}", a);
326+
(*target).a = a; // Update the value in RustObject with the value received from the callback
374327
}
375328
376-
impl Drop for ExtLibWrapper {
377-
fn drop(&mut self) {
378-
// Unregister to avoid further callbacks
379-
unsafe { unregister(); }
380-
}
329+
#[link(name = "extlib")]
330+
extern {
331+
fn register_callback(target: *RustObject, cb: extern "C" fn(*RustObject, i32)) -> i32;
332+
fn trigger_callback();
381333
}
382334
383-
extern "C" fn callback(target: *ExtLibWrapper, data: EventData) {
384-
unsafe {
385-
(*target).chan.send(data); // Forward the event data through channel
335+
fn main() {
336+
// Create the object that will be referenced in the callback
337+
let rust_object = ~RustObject{a: 5, ...};
338+
339+
unsafe {
340+
// Gets a raw pointer to the object
341+
let target_addr:*RustObject = ptr::to_unsafe_ptr(rust_object);
342+
register_callback(target_addr, callback);
343+
trigger_callback(); // Triggers the callback
386344
}
387345
}
388346
~~~~
389347

390-
C-Code:
348+
C code:
391349
~~~~
392-
typedef void (*rust_callback)(void* target, EventData data);
393-
void* rust_target;
350+
typedef void (*rust_callback)(int32_t);
351+
void* cb_target;
394352
rust_callback cb;
395-
mutex mtx; // Example mutex
396353
397-
void init(void* target, rust_callback callback) {
398-
rust_target = target;
354+
int32_t register_callback(void* callback_target, rust_callback callback) {
355+
cb_target = callback_target;
399356
cb = callback;
357+
return 1;
400358
}
401359
402-
void background_thread() {
403-
// do sth
404-
405-
// Lock the mutex to guarantee that callback is not performed after Rust
406-
// object is destroyed
407-
mutex_lock(mtx);
408-
if (rust_target != 0) cb(rust_target, event_data);
409-
mutex_unlock(mtx);
410-
411-
// do sth
412-
}
413-
414-
void unregister() {
415-
mutex_lock(mtx);
416-
rust_target = 0;
417-
mutex_unlock(mtx);
360+
void trigger_callback() {
361+
cb(cb_target, 7); // Will call callback(&rustObject, 7) in Rust
418362
}
419363
~~~~
420364

421-
Remark: This example will not work correctly if more than a single
422-
`ExtLibWrapper` object is created. If this is required additional handles
423-
have to be introduced which identify each object. E.g. `ExtLibWrapper` would
424-
have to store the member of the associated C object as member and pass it
425-
on each function call.
365+
## Asynchronous callbacks
366+
367+
In the already given examples the callbacks are invoked as a direct reaction
368+
to a function call to the external C library.
369+
The control over the current thread switched from Rust to C to Rust for the
370+
execution of the callback, but in the end the callback is executed on the
371+
same thread (and Rust task) that lead called the function which triggered
372+
the callback.
373+
374+
Things get more complicated when the external library spawns it's own threads
375+
and invokes callbacks from there.
376+
In these cases access to Rust data structures inside he callbacks is
377+
especially unsafe and proper synchronization mechanisms must be used.
378+
Besides classical synchronization mechanisms like mutexes one possibility in
379+
Rust is to use channels (in `std::comm`) to forward data from the C thread
380+
that invoked the callback into a Rust task.
381+
382+
If an asychronous callback targets a special object in the Rust address space
383+
it is also absolutely necessary that no more callbacks are performed by the
384+
C library after the respective Rust object get's destroyed.
385+
This can be achieved by unregistering the callback it the object's
386+
destructor and designing the library in a way that guarantees that no
387+
callback will be performed after unregistration.
426388

427389
# Linking
428390

0 commit comments

Comments
 (0)