All Manuals > Foreign Language Interface User Guide and Reference Manual > 5 Advanced Uses of the FLI

5.7 Block objects in C (foreign blocks)

This section applies to LispWorks for Macintosh, only.

Foreign blocks are objects that correspond to the opaque "Block" object in C and derived languages that are introduced in CLANG and used by Apple Computer, Inc.

A "Block" in C is similar to a closure in Lisp. It encapsulates a piece of code, and potentially some variables (which may be local), and allows invocation of this code.

LispWorks foreign blocks allows your Lisp program to call into and get called by code that uses blocks.

A foreign block is represented in LispWorks by a foreign pointer with pointer type foreign-block-pointer. Even though these are foreign pointers, these objects should be regarded as opaque, and should not be manipulated or used except as described below.

You use a foreign block by passing it to a foreign function that is defined to take a block as an argument, or by invoking a block that is received from a foreign function. The argument type needs to be specified as foreign-block-pointer.

When a foreign function invokes a block which was created in Lisp (or a copy of it), this invocation calls a Lisp function which the programmer supplied to the creating function or macro. When Lisp invokes a block that came from foreign code, it invokes some (unknown) foreign code.

Blocks can be used to run code via the Grand Central Dispatch mechanism (GCD) in macOS (see Apple documentation). There is a simple example in:

(example-edit-file "fli/grand-central-dispatch")

5.7.1 Calling foreign code that receives a block as argument

To call foreign code that needs a block as an argument, the Lisp program needs to create the blocks. You do this in two steps:

  1. At load time, define a "type" by using the macro define-foreign-block-callable-type. This "type" corresponds to the "signature" in C.
  2. At run time, generate the block, for example by calling allocate-foreign-block with the "type". Alternatively use one of the macros with-foreign-block and with-local-foreign-block. When generating the block, you also pass an arbitrary Lisp function that gets called when the block (or a copy of it) is invoked.

Foreign blocks created by allocate-foreign-block are released when appropriate by free-foreign-block.

Foreign block pointers created by allocate-foreign-block are of type foreign-block-pointer and print with "lisp-foreign-block-pointer".

For examples see:

(example-edit-file "fli/foreign-blocks")


(example-edit-file "fli/grand-central-dispatch")

5.7.2 Operations on foreign blocks

You might obtain a foreign pointer of type foreign-block-pointer that was passed as an argument to another foreign block, to a callable defined by define-foreign-callable or returned by a foreign function.

The foreign block can be invoked by defining an invoker (at load time) using define-foreign-block-invoker, and calling the invoker. If you need to keep the block after returning to the caller, you normally need to copy it by foreign-block-copy. If you copy a block, once you are finished with it, you should release it by foreign-block-release.

For examples of this see:

(example-edit-file "fli/invoke-foreign-block")

5.7.3 Scope of invocation

In principle, in the general case each of these is not defined:

The implementation of foreign blocks copes with all of these, that is it can work concurrently on any thread and after the block was released/freed, as long as there are live copies of it (except with blocks created by with-local-foreign-block). However, whether the code inside the block can cope with it is dependent on the code. This needs to be considered when creating blocks.

Specific foreign functions that take blocks as argument should be documented to state the scope of invocation. Apple's documentation commonly states whether the code is invoked concurrently or serially. In some functions the caller can decide when it calls the function whether the code can be executed concurrently or not. If you pass the block to a function that is documented to execute it serially, or you can tell it to do it, then you can assume that function that you made the block with is not going to be called concurrently from the block. Otherwise it must be able to cope with concurrent calls from the blocks.

Whether the code may be invoked on another thread or after the function that took the block returned is not normally documented. In many cases it can be deduced with confidence: when you dispatch a block to a queue (for example dispatch_after and similar functions, see the Apple documentation) it clearly can be invoked from another threads after the function returns. In the case of qsort_b (see Apple documentation and the example in (example-edit-file "fli/foreign-blocks")) we can be sure that the code will not be invoked after qsort_b returned, because the arguments to the block are based on the data (first argument to qsort_b), and qsort_b and its callees cannot be guaranteed that the data is still valid once qsort_b returned. On the other hand, we cannot be sure that the block is not invoked on another thread(s) before qsort_b returns. Currently it is probably always called in the same thread where qsort_b was called, but the interface does not guarantee it.

Thus when you create a foreign block in Lisp, the following considerations apply to the Lisp function function that you supply:

Foreign Language Interface User Guide and Reference Manual - 01 Dec 2021 19:34:57