Skip to content

Commit 62f8661

Browse files
committed
auto merge of #11486 : Matthias247/rust/doc, r=cmr
I wrote a chapter for the FFI tutorial that describes how to perform callbacks from C code to Rust and gives some hints about what to consider and synchronization. I just needed that for my own wrapper and thought it would be helpful for others.
2 parents 94236fc + 112d01a commit 62f8661

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

doc/guide-ffi.md

+137
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,143 @@ fn main() {
249249
}
250250
~~~~
251251

252+
# Callbacks from C code to Rust functions
253+
254+
Some external libraries require the usage of callbacks to report back their
255+
current state or intermediate data to the caller.
256+
It is possible to pass functions defined in Rust to an external library.
257+
The requirement for this is that the callback function is marked as `extern`
258+
with the correct calling convention to make it callable from C code.
259+
260+
The callback function that can then be sent to through a registration call
261+
to the C library and afterwards be invoked from there.
262+
263+
A basic example is:
264+
265+
Rust code:
266+
~~~~ {.xfail-test}
267+
extern fn callback(a:i32) {
268+
println!("I'm called from C with value {0}", a);
269+
}
270+
271+
#[link(name = "extlib")]
272+
extern {
273+
fn register_callback(cb: extern "C" fn(i32)) -> i32;
274+
fn trigger_callback();
275+
}
276+
277+
fn main() {
278+
unsafe {
279+
register_callback(callback);
280+
trigger_callback(); // Triggers the callback
281+
}
282+
}
283+
~~~~
284+
285+
C code:
286+
~~~~ {.xfail-test}
287+
typedef void (*rust_callback)(int32_t);
288+
rust_callback cb;
289+
290+
int32_t register_callback(rust_callback callback) {
291+
cb = callback;
292+
return 1;
293+
}
294+
295+
void trigger_callback() {
296+
cb(7); // Will call callback(7) in Rust
297+
}
298+
~~~~
299+
300+
In this example will Rust's `main()` will call `do_callback()` in C,
301+
which would call back to `callback()` in Rust.
302+
303+
304+
## Targetting callbacks to Rust objects
305+
306+
The former example showed how a global function can be called from C-Code.
307+
However it is often desired that the callback is targetted to a special
308+
Rust object. This could be the object that represents the wrapper for the
309+
respective C object.
310+
311+
This can be achieved by passing an unsafe pointer to the object down to the
312+
C library. The C library can then include the pointer to the Rust object in
313+
the notification. This will provide a unsafe possibility to access the
314+
referenced Rust object in callback.
315+
316+
Rust code:
317+
~~~~ {.xfail-test}
318+
319+
struct RustObject {
320+
a: i32,
321+
// other members
322+
}
323+
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
327+
}
328+
329+
#[link(name = "extlib")]
330+
extern {
331+
fn register_callback(target: *RustObject, cb: extern "C" fn(*RustObject, i32)) -> i32;
332+
fn trigger_callback();
333+
}
334+
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
344+
}
345+
}
346+
~~~~
347+
348+
C code:
349+
~~~~ {.xfail-test}
350+
typedef void (*rust_callback)(int32_t);
351+
void* cb_target;
352+
rust_callback cb;
353+
354+
int32_t register_callback(void* callback_target, rust_callback callback) {
355+
cb_target = callback_target;
356+
cb = callback;
357+
return 1;
358+
}
359+
360+
void trigger_callback() {
361+
cb(cb_target, 7); // Will call callback(&rustObject, 7) in Rust
362+
}
363+
~~~~
364+
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.
388+
252389
# Linking
253390

254391
The `link` attribute on `extern` blocks provides the basic building block for

0 commit comments

Comments
 (0)