@@ -249,6 +249,175 @@ fn main() {
249
249
}
250
250
~~~~
251
251
252
+ # Callbacks from C code to Rust functions
253
+
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.
257
+ It is possible to pass functions defined in Rust to an external library.
258
+ The requirement for this is that the callback function is marked as ` extern `
259
+ with the correct calling convention to make it callable from C code.
260
+
261
+ The callback function that can then be sent to through a registration call
262
+ to the C library and afterwards be invoked from there.
263
+
264
+ A basic example is:
265
+
266
+ Rust code:
267
+ ~~~~
268
+ extern "C" fn callback(a:i32) {
269
+ println!("I'm called from C with value {0}", a);
270
+ }
271
+
272
+ #[link(name = "extlib")]
273
+ extern {
274
+ fn register_callback(cb: extern "C" fn(i32)) -> i32;
275
+ }
276
+
277
+ fn main() {
278
+ unsafe {
279
+ register_callback(callback);
280
+ }
281
+ ... // Do sth. and wait for callbacks
282
+ }
283
+ ~~~~
284
+
285
+ C-Code:
286
+ ~~~~
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 thread() {
296
+ // do sth
297
+ cb(7); // Will call callback(7) in Rust
298
+ }
299
+ ~~~~
300
+
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.
304
+
305
+ ## Targetting callbacks to Rust objects
306
+
307
+ The former example showed how a global function can be called from C-Code.
308
+ However it is often desired that the callback is targetted to a special
309
+ Rust object. This could be the object that represents the wrapper for the
310
+ respective C object.
311
+
312
+ This can be achieved by passing an unsafe pointer to the object down to the
313
+ C library. The C library can then include the pointer to the Rust object in
314
+ the notification. This will provide a unsafe possibility to access the
315
+ referenced Rust object in callback.
316
+
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
+ Putting this together a wrapper for a library that uses a background thread
333
+ that sends events could look like:
334
+
335
+ Rust code:
336
+ ~~~~
337
+
338
+ #[link(name = "extlib")]
339
+ extern {
340
+ fn init(target: *ExtLibWrapper, cb: extern "C" fn(*ExtLibWrapper, EventData));
341
+ fn unregister();
342
+ }
343
+
344
+ struct EventData {
345
+ ... // Contains data that describes the event
346
+ }
347
+
348
+ pub struct ExtLibWrapper {
349
+ // Channel is used privately
350
+ priv chan: comm::Chan<EventData>,
351
+
352
+ // The port is used by the Rust task to receive notifications
353
+ port: comm::Port<EventData>
354
+ }
355
+
356
+ impl ExtLibWrapper {
357
+ pub fn new() -> ~ EventData {
358
+ let (p,c):(Port<EventData>,Chan<EventData>)
359
+ = comm::Chan::new();
360
+
361
+ let wrapper = ~ExtLibWrapper{chan:c, port:p};
362
+
363
+ unsafe {
364
+ let wrapper_addr:*ExtLibWrapper = ptr::to_unsafe_ptr(wrapper);
365
+ init(wrapper_addr, callback);
366
+ }
367
+ }
368
+ }
369
+
370
+ impl Drop for ExtLibWrapper {
371
+ fn drop(&mut self) {
372
+ // Unregister to avoid further callbacks
373
+ unsafe { unregister(); }
374
+ }
375
+ }
376
+
377
+ extern "C" fn callback(target: *ExtLibWrapper, data: EventData) {
378
+ unsafe {
379
+ (*target).chan.send(data); // Forward the event data through channel
380
+ }
381
+ }
382
+ ~~~~
383
+
384
+ C-Code:
385
+ ~~~~
386
+ typedef void (*rust_callback)(void* target, EventData data);
387
+ void* rust_target;
388
+ rust_callback cb;
389
+ mutex mtx; // Example mutex
390
+
391
+ void init(void* target, rust_callback callback) {
392
+ rust_target = target;
393
+ cb = callback;
394
+ }
395
+
396
+ void background_thread() {
397
+ // do sth
398
+
399
+ // Lock the mutex to guarantee that callback is not performed after Rust
400
+ // object is destroyed
401
+ mutex_lock(mtx);
402
+ if (rust_target != 0) cb(rust_target, event_data);
403
+ mutex_unlock(mtx);
404
+
405
+ // do sth
406
+ }
407
+
408
+ void unregister() {
409
+ mutex_lock(mtx);
410
+ rust_target = 0;
411
+ mutex_unlock(mtx);
412
+ }
413
+ ~~~~
414
+
415
+ Remark: This example will not work correctly if more than a single
416
+ ` ExtLibWrapper ` object is created. If this is required additional handles
417
+ have to be introduced which identify each object. E.g. ` ExtLibWrapper ` would
418
+ have to store the member of the associated C object as member and pass it
419
+ on each function call.
420
+
252
421
# Linking
253
422
254
423
The ` link ` attribute on ` extern ` blocks provides the basic building block for
0 commit comments