Chapter 1

Overview

This manual describes MzScheme's C interface, which allows the interpreter to be extended by a dynamically-loaded library, or embedded within an abitrary C/C++ program. The manual assumes familiarity with MzScheme, as described in PLT MzScheme: Language Manual.

1.1  Writing MzScheme Extensions

To write a C/C++-based extension for MzScheme, follow these steps:

IMPORTANT: Scheme values are garbage collected using a conservative garbage collector, so pointers to MzScheme objects can be kept in registers, stack variables, or structures allocated with scheme_malloc. However, static variables that contain pointers to collectable memory must be registered using scheme_register_extension_global (see section 3).

As an example, the following C code defines an extension that returns "hello world" when it is loaded:

 #include "escheme.h"
 Scheme_Object *scheme_initialize(Scheme_Env *env) {
   return scheme_make_string("hello world");
 }
 Scheme_Object *scheme_reload(Scheme_Env *env) {
   return scheme_initialize(env); /* Nothing special for reload */
 }
 Scheme_Object *scheme_module_name() {
   return scheme_false;
 }

Assuming that this code is in the file hw.c, the extension is compiled under Unix with the following two commands:

mzc --cc hw.c
mzc --ld hw.so hw.o

(Note that the --cc and --ld flags are each prefixed by two dashes, not one.)

The plt/collects/mzscheme/examples directory in the PLT distribution contains additional examples.

1.2  Embedding MzScheme into a Program

To embed MzScheme in a program, first download the MzScheme source code. Then, follow these steps:

Scheme values are garbage collected using a conservative garbage collector, so pointers to MzScheme objects can be kept in registers, stack variables, or structures allocated with scheme_malloc. In an embedding application, static variables are also automatically registered as roots for garbage collection (but see the Windows-specific note below).

For example, the following is a simple embedding program which evaluates all expressions provided on the command line and displays the results, then runs a read-eval-print loop:

#include "scheme.h"
int main(int argc, char *argv[])
{
  Scheme_Env *e = scheme_basic_env();
  Scheme_Object *curout = scheme_get_param(scheme_config, MZCONFIG_OUTPUT_PORT);
  int i;
  for (i = 1; i < argc; i++) {
    if (scheme_setjmp(scheme_error_buf)) {
      return -1; /* There was an error */
    } else {
      Scheme_Object *v = scheme_eval_string(argv[i], e);
      scheme_display(v, curout);
      scheme_display(scheme_make_character('\n'), curout);
      /* read-eval-print loop, implicitly uses the initial Scheme_Env: */
      scheme_apply(scheme_builtin_value("read-eval-print-loop"), 0, NULL);
    }
  }
  return 0;
}

Under Windows, the garbage collector finds static variables in an embeddeding program by examining all memory pages. This strategy fails if a program contains multiple Windows threads; a page may get unmapped by a thread while the collector is examining the page, causing the collector to crash. To avoid this problem, call scheme_set_stack_base with a non-zero second argument before calling any scheme_ function, and register all globals with scheme_register_static.

1.3  MzScheme and Threads

MzScheme implements threads for Scheme programs without aid from the operating system. MzScheme can co-exist with additional OS-implemented threads that are created by an extension or an embedding program, but the additional OS threads must not call any scheme_ function. Only the OS thread that originally calls scheme_basic_env can call scheme_ functions.2

When scheme_basic_env is called a second time to reset the interpreter, it can be called in an OS thread that is different from the original call to scheme_basic_env. Thereafter, all calls to scheme_ functions must originate from the new thread.

See section 8 for more information about threads, including the possible effects of MzScheme's thread implementation on extension and embedding C code.


1 The C preprocessor symbol SCHEME_DIRECT_EMBEDDED is defined as 1 when scheme.h is #included, or as 0 when escheme.h is #included.

2 This restriction is stronger than saying all calls must be serialized across threads. MzScheme relies on properties of specific threads to avoid stack overflow and garbage collection.