On this page:
9.1 CGC Embedding
9.2 3m Embedding
9.3 Flags and Hooks

9 Embedding into a Program (BC)🔗

The Racket run-time system can be embedded into a larger program. The embedding process for Racket CGC or Racket 3m (see CGC versus 3m) is essentially the same, but the process for Racket 3m is most easily understood as a variant of the process for Racket CGC (even though Racket 3m is the standard variant of Racket).

9.1 CGC Embedding🔗

To embed Racket CGC in a program, follow these steps:

With Racket CGC, Racket values are garbage collected using a conservative garbage collector, so pointers to Racket objects can be kept in registers, stack variables, or structures allocated with scheme_malloc. In an embedding application on some platforms, static variables are also automatically registered as roots for garbage collection (but see notes below specific to Mac OS and Windows).

For example, the following is a simple embedding program that runs a module "run.rkt", assuming that "run.c" is created as

  raco ctool --c-mods run.c "run.rkt"

to generate "run.c", which encapsulates the compiled form of "run.rkt" and all of its transitive imports (so that they need not be found separately a run time).

"main.c"

  #include "scheme.h"

  #include "run.c"

  

  static int run(Scheme_Env *e, int argc, char *argv[])

  {

    Scheme_Object *a[2];

  

    /* Declare embedded modules in "run.c": */

    declare_modules(e);

  

    a[0] = scheme_make_pair(scheme_intern_symbol("quote"),

                            scheme_make_pair(scheme_intern_symbol("run"),

                                             scheme_make_null()));

    a[1] = scheme_false;

  

    scheme_dynamic_require(2, a);

  

    return 0;

  }

  

  int main(int argc, char *argv[])

  {

    return scheme_main_setup(1, run, argc, argv);

  }

As another example, the following is a simple embedding program that evaluates all expressions provided on the command line and displays the results, then runs a read-eval-print loop, all using racket/base. Run

  raco ctool --c-mods base.c ++lib racket/base

to generate "base.c", which encapsulates racket/base and all of its transitive imports.

"main.c"

  #include "scheme.h"

  #include "base.c"

  

  static int run(Scheme_Env *e, int argc, char *argv[])

  {

    Scheme_Object *curout;

    int i;

    Scheme_Thread *th;

    mz_jmp_buf * volatile save, fresh;

  

    /* Declare embedded modules in "base.c": */

    declare_modules(e);

  

    scheme_namespace_require(scheme_intern_symbol("racket/base"));

  

    curout = scheme_get_param(scheme_current_config(),

                              MZCONFIG_OUTPUT_PORT);

  

    th = scheme_get_current_thread();

  

    for (i = 1; i < argc; i++) {

      save = th->error_buf;

      th->error_buf = &fresh;

      if (scheme_setjmp(*th->error_buf)) {

        th->error_buf = save;

        return -1; /* There was an error */

      } else {

        Scheme_Object *v, *a[2];

        v = scheme_eval_string(argv[i], e);

        scheme_display(v, curout);

        scheme_display(scheme_make_char('\n'), curout);

        /* read-eval-print loop, uses initial Scheme_Env: */

        a[0] = scheme_intern_symbol("racket/base");

        a[1] = scheme_intern_symbol("read-eval-print-loop");

        scheme_apply(scheme_dynamic_require(2, a), 0, NULL);

        th->error_buf = save;

      }

    }

    return 0;

  }

  

  int main(int argc, char *argv[])

  {

    return scheme_main_setup(1, run, argc, argv);

  }

If modules embedded in the executable need to access runtime files (via racket/runtime-path forms), supply the --runtime flag to raco ctool, specifying a directory where the runtime files are to be gathered. The modules in the generated ".c" file will then refer to the files in that directory; the directory is normally specified relative to the executable, but the embedding application must call scheme_set_exec_cmd to set the executable path (typically argv[0]) before declaring modules.

On Mac OS, or on Windows when Racket is compiled to a DLL using Cygwin, the garbage collector cannot find static variables automatically. In that case, scheme_main_setup must be called with a non-zero first argument.

On Windows (for any other build mode), the garbage collector finds static variables in an embedding 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_main_setup with a non-zero first argument.

When an embedding application calls scheme_main_setup with a non-zero first argument, it must register each of its static variables with MZ_REGISTER_STATIC if the variable can contain a GCable pointer. For example, if curout above is made static, then MZ_REGISTER_STATIC(curout) should be inserted before the call to scheme_get_param.

When building an embedded Racket CGC to use SenoraGC (SGC) instead of the default collector, scheme_main_setup must be called with a non-zero first argument. See Memory Allocation (BC) for more information.

9.2 3m Embedding🔗

Racket 3m can be embedded mostly the same as Racket, as long as the embedding program cooperates with the precise garbage collector as described in Cooperating with 3m.

In either your source in the in compiler command line, #define MZ_PRECISE_GC before including "scheme.h". When using raco ctool with the --cc and --3m flags, MZ_PRECISE_GC is automatically defined.

In addition, some library details are different:

For Racket 3m, an embedding application must call scheme_main_setup with a non-zero first argument.

The simple embedding programs from the previous section can be processed by raco ctool --xform, then compiled and linked with Racket 3m. Alternately, the source code can be extended to work with either CGC or 3m depending on whether MZ_PRECISE_GC is defined on the compiler’s command line:

"main.c"

  #include "scheme.h"

  #include "run.c"

  

  static int run(Scheme_Env *e, int argc, char *argv[])

  {

    Scheme_Object *l = NULL;

    Scheme_Object *a[2] = { NULL, NULL };

  

    MZ_GC_DECL_REG(5);

    MZ_GC_VAR_IN_REG(0, e);

    MZ_GC_VAR_IN_REG(1, l);

    MZ_GC_ARRAY_VAR_IN_REG(2, a, 2);

  

    MZ_GC_REG();

  

    declare_modules(e);

  

    l = scheme_make_null();

    l = scheme_make_pair(scheme_intern_symbol("run"), l);

    l = scheme_make_pair(scheme_intern_symbol("quote"), l);

  

    a[0] = l;

    a[1] = scheme_false;

  

    scheme_dynamic_require(2, a);

  

    MZ_GC_UNREG();

  

    return 0;

  }

  

  int main(int argc, char *argv[])

  {

    return scheme_main_setup(1, run, argc, argv);

  }

"main.c"

  #include "scheme.h"

  #include "base.c"

  

  static int run(Scheme_Env *e, int argc, char *argv[])

  {

    Scheme_Object *curout = NULL, *v = NULL, *a[2] = {NULL, NULL};

    Scheme_Config *config = NULL;

    int i;

    Scheme_Thread *th = NULL;

    mz_jmp_buf * volatile save = NULL, fresh;

  

    MZ_GC_DECL_REG(9);

    MZ_GC_VAR_IN_REG(0, e);

    MZ_GC_VAR_IN_REG(1, curout);

    MZ_GC_VAR_IN_REG(2, save);

    MZ_GC_VAR_IN_REG(3, config);

    MZ_GC_VAR_IN_REG(4, v);

    MZ_GC_VAR_IN_REG(5, th);

    MZ_GC_ARRAY_VAR_IN_REG(6, a, 2);

  

    MZ_GC_REG();

  

    declare_modules(e);

  

    v = scheme_intern_symbol("racket/base");

    scheme_namespace_require(v);

  

    config = scheme_current_config();

    curout = scheme_get_param(config, MZCONFIG_OUTPUT_PORT);

  

    th = scheme_get_current_thread();

  

    for (i = 1; i < argc; i++) {

      save = th->error_buf;

      th->error_buf = &fresh;

      if (scheme_setjmp(*th->error_buf)) {

        th->error_buf = save;

        return -1; /* There was an error */

      } else {

        v = scheme_eval_string(argv[i], e);

        scheme_display(v, curout);

        v = scheme_make_char('\n');

        scheme_display(v, curout);

        /* read-eval-print loop, uses initial Scheme_Env: */

        a[0] = scheme_intern_symbol("racket/base");

        a[1] = scheme_intern_symbol("read-eval-print-loop");

        v = scheme_dynamic_require(2, a);

        scheme_apply(v, 0, NULL);

        th->error_buf = save;

      }

    }

  

    MZ_GC_UNREG();

  

    return 0;

  }

  

  int main(int argc, char *argv[])

  {

    return scheme_main_setup(1, run, argc, argv);

  }

Strictly speaking, the config and v variables above need not be registered with the garbage collector, since their values are not needed across function calls that allocate. The code is much easier to maintain, however, when all local variables are registered and when all temporary values are put into variables.

9.3 Flags and Hooks🔗

The following flags and hooks are available when Racket is embedded:

void

 

scheme_set_collects_path

(

Scheme_Object* path)

Sets the path to be returned by (find-system-path 'collects-dir).

void

 

scheme_set_addon_path

(

Scheme_Object* path)

Sets the path to be returned by (find-system-path 'addon-dir).

void

 

scheme_set_exec_cmd

(

const char* path)

Sets the path to be returned by (find-system-path 'exec-file).

void

scheme_init_collection_paths_post

(

Scheme_Env* env,

 

 

Scheme_Object* pre_extra_paths,

 

 

Scheme_Object* post_extra_paths)

Initializes the current-library-collection-paths parameter using find-library-collection-paths. The pre_extra_paths and post_extra-paths arguments are propagated to find-library-collection-paths.

The function calls scheme_seal_parameters automatically.

void

 

scheme_init_collection_paths

(

Scheme_Env* env,

 

 

 

 

Scheme_Object* pre_extra_paths)

Like scheme_init_collection_paths_post, but with null as the last argument.

void

 

scheme_set_dll_path

(

wchar_t* path)

On Windows only, sets the path used to find optional DLLs that are used by the runtime system: "longdouble.dll" and one of "iconv.dll", "libiconv.dll", or "libiconv-2.dll". The given path should be an absolute path.

Takes a snapshot of the current values of built-in parameters. These values are used for privileged actions, such as installing a PLaneT package.