1.4 Embedding ECL

1.4.1 Minimal Example

An example project is included in the ECL source distribution in the examples/embed/ directory.

This example consists of a Common Lisp library (hello-lisp.lisp) and a system definition (hello-lisp.asd, See Compiling with ASDF) that is called from a C program (hello.c). The example Makefile shows how to build a static library from the Lisp library and link it with the C program.


1.4.2 Embedding Reference


1.4.2.1 Starting and Stopping

Function: int cl_boot (int argc, char **argv);

Setup the lisp environment.

argc

An integer with the number of arguments to this program.

argv

A vector of strings with the arguments to this program.

Description

This function must be called before any other function from the ECL library, including the creation of any lisp object or evaluating any lisp code. The only exception are ecl_set_option and ecl_get_option.

Function: int cl_shutdown (void);

Close the lisp environment.

Description

This function must be called before exiting a program that uses the ECL environment. It performs some cleaning, including the execution of any finalizers, unloading shared libraries and deleting temporary files that were created by the compiler.

Function: void ecl_set_option (int option, cl_fixnum value);

Set a boot option.

option

An integer from Table 1.1.

value

A cl_index value for this option

Description

This functions sets the value of different options that have to be customized before ECL boots. The table of options and default values [Table 1.1] shows that some of them are boolean, and some of them are unsigned integers.

We distinguish three sets of values. The first set determines whether ECL handles certain exceptions, such as access to forbidden regions of memory, interrupts via , floating point exceptions, etc.

The second set is related to the sizes of different stacks. Currently ECL uses four stacks: a bind stack for keeping assignments to special variables; a frame stack for implementing blocks, tagbodys and catch points; an interpreter stack for evaluating bytecodes, and finally the machine or C stack, of the computer we run in. We can set the expected size of these stacks, together with the size of a safety area which, if penetrated, will lead to the generation of a correctable error.

Name (ECL_OPT_*)TypeDefaultDescription
INCREMENTAL_GCbooleanTRUEActivate generational garbage collector.
TRAP_SIGSEGVbooleanTRUECapture SIGSEGV signals.
TRAP_SIGFPEbooleanTRUECapture floating point exceptions.
TRAP_SIGINTbooleanTRUECapture user interrupts.
TRAP_SIGILLbooleanTRUECapture SIGILL exception.
TRAP_INTERRUPT_SIGNALbooleanTRUECapture the signal that implements mp:interrupt-process.
SIGNAL_HANDLING_THREADbooleanTRUECreate a signal to capture and process asynchronous threads (See Asynchronous signals).
BOOTEDbooleanTRUE/FALSEHas ECL booted (read only).
BIND_STACK_SIZEcl_index8192Size of stack for binding special variables.
BIND_STACK_SAFETY_AREAcl_index128
FRAME_STACK_SIZEcl_index2048Size of stack for nonlocal jumps.
FRAME_STACK_SAFETY_AREAcl_index128
LISP_STACK_SIZEcl_index32768Size of interpreter stack.
LISP_STACK_SAFETY_AREAcl_index128
C_STACK_SIZEcl_index0 or 1048576Size of C stack in bytes. The effect and default value of this option depends on the operating system. On Unix, the default is 0 which means that ECL will use the stack size provided by the OS. If set to a non-default value, ECL will set the stack size to the given value unless the stack size provided by the OS is already large enough. On Windows, the stack size is set at build time and cannot be changed at runtime. Here, we use a default of 1 MiB. For other operating systems, it is up to the user to set this value to the available stack size so that ECL can reliably detect stack overflows.
C_STACK_SAFETY_AREAcl_index4192
THREAD_INTERRUPT_SIGNALunsigned int0If nonzero, specify the unix signal which is used to communicate between different Lisp threads.

Table 1.1: Boot options for embedded ECL

Function: cl_fixnum ecl_get_option (int option);

Read the value of a boot option.

option

An integer from Table 1.1.

Description

This functions reads the value of different options that have to be customized before ECL boots. The table of options and default values is Table 1.1.

Function: bool ecl_import_current_thread (cl_object name, cl_object bindings);

Import an external thread in the Lisp environment.

name

Thread name.

bindings

Unused (specifying initial bindings for external threads is not supported currently)

returns

True if the thread was successfully imported, false otherwise.

Description

External threads, i.e. threads which are not created in the Lisp world using the routines described in Processes (native threads), need to be imported with ecl_import_current_thread before Lisp code can be executed.

See also

ecl_release_current_thread

Function: void ecl_release_current_thread (void);

Release an external thread imported with ecl_import_current_thread. Must be called before thread exit to prevent memory leaks.

Environment variable: ECLDIR

Specify a non-standard installation directory.

Description

ECL includes various files for external modules (e.g. asdf, sockets), character encodings or documentation strings. The installation directory for these files is chosen during build time by the configure script. If the directory is moved to a different place, the ECLDIR environment variable should be updated accordingly. Note that the contents of the variable are parsed as a Common Lisp pathname, thus it must end with a slash.


1.4.2.2 Catching Errors and Managing Interrupts

Macro: ECL_CATCH_ALL

Create a protected region.

C Macro

cl_env_ptr env = ecl_process_env();
ECL_CATCH_ALL_BEGIN(env) {
  /*
   * Code that is protected. Uncaught lisp conditions, THROW,
   * signals such as SIGSEGV and SIGBUS may cause jump to
   * this region.
   */
} ECL_CATCH_ALL_IF_CAUGHT {
  /*
   * If the exception, lisp condition or other control transfer
   * is caught, this code is executed.
   */
} ECL_CATCH_ALL_END
/*
 * In all cases we exit here.
 */

Description

This is a set of three macros that create an unwind-protect region that prevents any nonlocal transfer of control to outer loops. In the Lisp speak, the previous code is equivalent to

(block nil
  (unwind-protect
     (progn
        ;; Code that is protected
	)
    (return nil)))

As explained in ECL_UNWIND_PROTECT, it is normally advisable to set up an unwind-protect frame to avoid the embedded lisp code to perform arbitrary transfers of control.

See also

ECL_UNWIND_PROTECT

Macro: ECL_UNWIND_PROTECT

Create a protected region.

C Macro

cl_env_ptr env = ecl_process_env();
ECL_UNWIND_PROTECT_BEGIN(env) {
  /*
   * Code that is protected. Uncaught lisp conditions, THROW,
   * signals such as SIGSEGV and SIGBUS may cause jump to
   * this region.
   */
} ECL_UNWIND_PROTECT_EXIT {
  /*
   * If the exception, lisp condition or other control transfer
   * is caught, this code is executed. After this code, the
   * process will jump to the original destination of the
   * THROW, GOTO or other control statement that was interrupted.
   */
} ECL_UNWIND_PROTECT_END
/*
 * We only exit here if NO nonlocal jump was interrupted.
 */

Description

When embedding ECL it is normally advisable to set up an unwind-protect frame to avoid the embedded lisp code to perform arbitrary transfers of control. Furthermore, the unwind protect form will be used in at least in the following occasions:

Besides this, normal mechanisms for exit, such as ext:quit, and uncaught exceptions, such as serious signals (See Synchronous signals), are best handled using unwind-protect blocks.

See also

ECL_CATCH_ALL

Macro: ecl_clear_interrupts ()

Clear all pending signals and exceptions.

Description

This macro clears all pending interrupts.

See also

ecl_disable_interrupts and ecl_enable_interrupts.

Macro: ecl_disable_interrupts ()

Postpone handling of signals and exceptions.

Description

This macro sets a thread-local flag indicating that all received signals should be queued for later processing. Note that it is not possible to execute lisp code while interrupts are disabled in this way. For this purpose, use the mp:without-interrupts macro. Every call to ecl_disable_interrupts must be followed by a corresponding call to ecl_enable_interrupts, otherwise race conditions will appear.

See also

ecl_enable_interrupts and ecl_clear_interrupts.

Macro: ecl_enable_interrupts ();

Activate handling of signals and exceptions.

Description

This macro sets a thread-local flag indicating that all received signals can be handled. If there are any pending signals, they will be immediately processed.

See also

ecl_disable_interrupts and ecl_clear_interrupts.

Macro: ECL_WITH_LISP_FPE

Execute Lisp code with correct floating point environment

Description

Unless floating point exceptions are disabled (via the --without-fpe configure option or ECL_OPT_TRAP_SIGFPE runtime option), ECL will change the floating point environment when booting. This macro allows for execution of Lisp code while saving and later restoring the floating point environment of surrounding C code so that changes in the floating point environment don’t leak outside.

ECL_WITH_LISP_FPE can be also used before ECL has booted.

Example

#include <ecl/ecl.h>
#include <stdio.h>

int main(int argc, char **argv) {
  ECL_WITH_LISP_FPE_BEGIN {
    cl_boot(argc, argv);
  } ECL_WITH_LISP_FPE_END;

  double a = 1.0 / 0.0;
  double b;

  ECL_WITH_LISP_FPE_BEGIN {
    cl_object form = ecl_read_from_cstring("(handler-case"
                                               "(/ 1d0 0d0)"
                                             "(division-by-zero () 0d0))");
    b = ecl_to_double(si_safe_eval(3, form, ECL_NIL, ECL_NIL));
  } ECL_WITH_LISP_FPE_END;

  printf("%g %g\n", a, b);

  cl_shutdown();
  return 0;
}

will output

inf 0

See also

ext:trap-fpe