2.6 Foreign Procedures and Callbacks
ffi2 type
(-> arg-or-do ... maybe-varargs result maybe-errno option ...)
arg-or-do = arg | #:do [defn-or-expr ...] arg = type | [arg-id : type] | [type = auto-expr] | [arg-id : type = auto-expr] maybe-varargs =
| #:varargs arg-or-do ... result = result-type | [result-id : result-type] maybe-errno =
| #:errno | #:get-last-error | [errno-id : #:errno] | [errno-id : #:get-last-error] option = #:result result-expr | #:abi abi | #:atomic | #:collect-safe | #:allow-callback-exn | #:in-original
The racket/contract library also provides ->. To avoid conflicts, consider renaming the racket/contract binding to ->/c on import.
A -> type is normally used with ffi2-procedure or define-ffi2-procedure to obtain a Racket produce that calls a C function. That use of -> creates a foreign callout.
A -> type can also be used with ffi2-callback to turn a Racket procedure into a function callback by C as represented by a pointer object. That of use of -> creates a foreign callback. A callback always runs in atomic mode, which means that it must not attempt any synchronization operations and generally must not raise an exception (unless #:allow-callback-exn is used for a callout that reaches the callback). If a callback is run in an operating-system thread that was not started by Racket, then the callback is subject to the same constraints as a procedure passed to call-in-os-thread.
Each arg describes a type for an argument, each with an optional name arg-id and an optional auto-expr. When an argument has an auto-expr, no corresponding argument is provided to a callout. Each auto-expr can refer to arg-ids for arguments without auto-exprs and to earlier arguments that have auto-exprs. When an arrow type is used for a foreign callback (instead of a callout), arg-ids and auto-exprs have no effect.
Each #:do form among the args inserts additional expressions and definitions that are evaluated similar to auto-exprs, that can refer to earlier argument names, to argument names without auto-exprs, and to definitions from earlier #:do forms. An auto-expr can also refer to definitions from preceding #:do forms.
A result can have a result-id. That identifier can be used (along with the arg-ids) in a result-expr supplied with #:result. The value of #:result becomes the result for a callout, and it is not used for a callback.
If #:errno or #:get-last-error (optionally with a errno-id) is specified after result-type, then calling a function produced by ffi2-procedure returns two values (absent a #:result expression): the foreign procedure’s return value and the value of the C library’s errno or the result of the GetLastError function on Windows. The errno or GetLastError result is obtained just after the foreign procedure call returns and before it can be changed. On platforms other than Windows, #:get-last-error is treated as #:errno. A #:errno or #:get-last-error specification has no effect for a callback. If errno-id is specified, it can be referenced in a result-expr to determine a callout result.
Each option affects the way a foreign procedure is called or how a callback is handled:
#:abi: Uses abi as the ABI for a foreign callout or foreign callback. See default_abi, cdecl_abi, and stdcall_abi.
#:atomic: Adjusts a foreign callout to potentially improve performance. The foreign procedure must not invoke any callbacks or otherwise reach the Racket run-time system, so it can be considered an atomic operation from the perspective of Racket. This option has no effect on foreign callbacks.
#:collect-safe: Adjusts a foreign callout to allow Racket garbage collection concurrent with the call, or adjusts a foreign callback to re-enable synchronization with the garbage collector during the callback (i.e., only collect-safe callbacks are allowed to be invoked via a collect-safe procedure call; a collect-safe callback can be invoked through a non-collect-safe foreign procedure call). Note that a collect-safe call makes sense only when arguments to the foreign procedure are not managed by the Racket garbage collector or are immobile and reliably retained.
#:allow-callback-exn: Adjusts a foreign callout so that a foreign callback is allowed to raise an exception that escape the foreign-procedure call. This option has no effect on foreign callbacks themselves. A foreign callback must never raise an exception unless it is invoked via foreign procedure call using this option.
#:in-original: Adjusts a foreign callout to run in a coroutine thread within Racket’s main place (which is useful if the foreign procedure is not thread-safe), or adjusts a foreign callback invocation so that it runs in a coroutine thread within the current place (which can be useful if the callback might otherwise run in a thread not created by Racket).In the BC variant of Racket, #:in-original is required if a callback may run in a thread not created by Racket. The callout or callback happens in the context of an unspecified Racket coroutine thread, so it must not raise an exception, and it is still in atomic mode. The callback blocks the original thread until it completes in a coroutine thread.
syntax
(ffi2-procedure ptr-expr arrow-type)
Using ffi2-procedure is equivalent to (ffi2-cast ptr-expr #:from ptr_t #:to arrow-type) to convert a foreign function pointer to a procedure that can be called from Racket.
syntax
(ffi2-callback proc-expr arrow-type)
Using ffi2-callback is equivalent to (ffi2-cast proc-expr #:from arrow-type #:to ptr_t) to convert a Racket procedure to a function pointer to be called in a foreign context.
A foreign callback is managed by the Racket garbage collector. A callback is always immobile in the sense of using ffi2-malloc in #:gcable-immobile mode, but the callback must be retained somehow as long as it is relevant for calls from a foreign context. A callback is automatically retained in that sense if it is passed to a foreign callout that uses the callback only until the callout returns.
syntax
(define-ffi2-procedure id arrow-type option ...)
option = #:lib lib-expr | #:c-id c-id | #:fail fail-expr | #:wrap wrap-expr
If c-id (or id) is not exported from ffi-lib, then if fail-expr is called with 'id as its argument, and id is defined as that result instead of a callout based on arrow-type.
If wrap-expr is provided, then the value that otherwise would be bound to id is passed to the result of wrap-expr, and id is defined as the result of that call.
syntax
(define-ffi2-definer id option ...)
option = #:lib lib-expr | #:default-fail fail-expr | #:default-wrap wrap-expr | #:provide
If #:default-fail or #:default-wrap are provided, they supply expressions that are used as #:fail or #:wrap defaults for id.
If #:provide is specified, then a name defined by id is also exported using provide with protect-out.
procedure
(make-not-available name) → procedure?
name : symbol?
ffi2 abi
ffi2 abi
ffi2 abi
syntax
(define-ffi2-abi name abi)
Use define-ffi2-abi to define an alias for an ABI. The system-type-case form works as an ABI to support a platform-dependent choice.