#lang scribble/doc @(require "utils.ss") @title{Overview} @section{CGC versus 3m} Before mixing any C code with MzScheme, first decide whether to use the @bold{3m} variant of PLT Scheme, the @bold{CGC} variant of PLT Scheme, or both: @itemize{ @item{@bold{@as-index{3m}} : the main variant of PLT Scheme, which uses @defterm{precise} garbage collection instead of conservative garbage collection, and it may move objects in memory during a collection.} @item{@bold{@as-index{CGC}} : the original variant of PLT Scheme, where memory management depends on a @defterm{conservative} garbage collector. The conservative garbage collector can automatically find references to managed values from C local variables and (on some platforms) static variables.} } At the C level, working with CGC can be much easier than working with 3m, but overall system performance is typically better with 3m. @; ---------------------------------------------------------------------- @section{Writing MzScheme Extensions} @section-index["extending MzScheme"] The process of creating an extension for 3m or CGC is essentially the same, but the process for 3m is most easily understood as a variant of the process for CGC. @subsection{CGC Extensions} To write a C/C++-based extension for PLT Scheme CGC, follow these steps: @itemize{ @item{@index['("header files")]{For} each C/C++ file that uses PLT Scheme library functions, @cpp{#include} the file @as-index{@filepath{escheme.h}}. This file is distributed with the PLT software in an @filepath{include} directory, but if @|mzc| is used to compile, this path is found automatically.} @item{Define the C function @cppi{scheme_initialize}, which takes a @cpp{Scheme_Env*} namespace (see @secref["im:env"]) and returns a @cpp{Scheme_Object*} Scheme value. This initialization function can install new global primitive procedures or other values into the namespace, or it can simply return a Scheme value. The initialization function is called when the extension is loaded with @scheme[load-extension] (the first time); the return value from @cpp{scheme_initialize} is used as the return value for @scheme[load-extension]. The namespace provided to @cpp{scheme_initialize} is the current namespace when @scheme[load-extension] is called.} @item{Define the C function @cppi{scheme_reload}, which has the same arguments and return type as @cpp{scheme_initialize}. This function is called if @scheme[load-extension] is called a second time (or more times) for an extension. Like @cpp{scheme_initialize}, the return value from this function is the return value for @scheme[load-extension].} @item{Define the C function @cppi{scheme_module_name}, which takes no arguments and returns a @cpp{Scheme_Object*} value, either a symbol or @cpp{scheme_false}. The function should return a symbol when the effect of calling @cpp{scheme_initialize} and @cpp{scheme_reload} is only to declare a module with the returned name. This function is called when the extension is loaded to satisfy a @scheme[require] declaration. The @cpp{scheme_module_name} function may be called before @cpp{scheme_initialize} and @cpp{scheme_reload}, after those functions, or both before and after, depending on how the extension is loaded and re-loaded.} @item{Compile the extension C/C++ files to create platform-specific object files. The @as-index{@|mzc|} compiler, which is distributed with PLT Scheme, compiles plain C files when the @as-index{@DFlag{cc}} flag is specified. More precisely, @|mzc| does not compile the files itself, but it locates a C compiler on the system and launches it with the appropriate compilation flags. If the platform is a relatively standard Unix system, a Windows system with either Microsoft's C compiler or @exec{gcc} in the path, or a Mac OS X system with Apple's developer tools installed, then using @|mzc| is typically easier than working with the C compiler directly. Use the @as-index{@DFlag{cgc}} flag to indicate that the build is for use with PLT Scheme CGC.} @item{Link the extension C/C++ files with @as-index{@filepath{mzdyn.o}} (Unix, Mac OS X) or @as-index{@filepath{mzdyn.obj}} (Windows) to create a shared object. The resulting shared object should use the extension @filepath{.so} (Unix), @filepath{.dll} (Windows), or @filepath{.dylib} (Mac OS X). The @filepath{mzdyn} object file is distributed in the installation's @filepath{lib} directory. For Windows, the object file is in a compiler-specific sub-directory of @filepath{plt\lib}. The @|mzc| compiler links object files into an extension when the @as-index{@DFlag{ld}} flag is specified, automatically locating @filepath{mzdyn}. Again, use the @DFlag{cgc} flag with @|mzc|.} @item{Load the shared object within Scheme using @scheme[(load-extension _path)], where @scheme[_path] is the name of the extension file generated in the previous step. Alternately, if the extension defines a module (i.e., @cpp{scheme_module_name} returns a symbol), then place the shared object in a special directory so that it is detected by the module loader when @scheme[require] is used. The special directory is a platform-specific path that can be obtained by evaluating @scheme[(build-path "compiled" "native" (system-library-subpath))]; see @scheme[load/use-compiled] for more information. For example, if the shared object's name is @filepath{example.dll}, then @scheme[(require "example.ss")] will be redirected to @filepath{example.dll} if the latter is placed in the sub-directory @scheme[(build-path "compiled" "native" (system-library-subpath))] and if @filepath{example.ss} does not exist or has an earlier timestamp. Note that @scheme[(load-extension _path)] within a @scheme[module] does @italic{not} introduce the extension's definitions into the module, because @scheme[load-extension] is a run-time operation. To introduce an extension's bindings into a module, make sure that the extension defines a module, put the extension in the platform-specific location as described above, and use @scheme[require].} } @index['("allocation")]{@bold{IMPORTANT:}} With PLT Scheme CGC, Scheme values are garbage collected using a conservative garbage collector, so pointers to Scheme objects can be kept in registers, stack variables, or structures allocated with @cppi{scheme_malloc}. However, static variables that contain pointers to collectable memory must be registered using @cppi{scheme_register_extension_global} (see @secref["im:memoryalloc"]). As an example, the following C code defines an extension that returns @scheme["hello world"] when it is loaded: @verbatim[#<error_buf; scheme_current_thread->error_buf = &fresh; if (scheme_setjmp(scheme_error_buf)) { scheme_current_thread->error_buf = save; 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, uses initial Scheme_Env: */ scheme_apply(scheme_builtin_value("read-eval-print-loop"), 0, NULL); scheme_current_thread->error_buf = save; } } return 0; } EOS ] Under Mac OS X, or under Windows when MzScheme is compiled to a DLL using Cygwin, the garbage collector cannot find static variables automatically. In that case, @cppi{scheme_set_stack_base} must be called with a non-zero second argument before calling any @cpp{scheme_} function. Under 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 @cpp{scheme_set_stack_base} with a non-zero second argument before calling any @cpp{scheme_} function. When an embedding application calls @cpp{scheme_set_stack_base} with a non-zero second argument, it must register each of its static variables with @cppi{MZ_REGISTER_STATIC} if the variable can contain a GCable pointer. For example, if @cpp{e} above is made @cpp{static}, then @cpp{MZ_REGISTER_STATIC(e)} should be inserted before the call to @cpp{scheme_basic_env}. When building an embedded MzSchemeCGC to use SenoraGC (SGC) instead of the default collector, @cpp{scheme_set_stack_base} must be called both with a non-zero second argument and with a stack-base pointer in the first argument. See @secref["im:memoryalloc"] for more information. @subsection{3m Embedding} MzScheme3m can be embedded mostly the same as MzScheme, as long as the embedding program cooperates with the precise garbage collector as described in @secref["im:3m"]. In either your source in the in compiler command line, @cpp{#define} @cpp{MZ_PRECISE_GC} before including @filepath{scheme.h}. When using @|mzc| with the @DFlag{cc} and @DFlag{3m} flags, @cpp{MZ_PRECISE_GC} is automatically defined. In addition, some library details are different: @itemize{ @item{Under Unix, the library is just @as-index{@filepath{libmzscheme3m.a}} (or @as-index{@filepath{libmzscheme3m.so}} for a dynamic-library build, with @as-index{@filepath{libmzscheme3m.la}} for use with @exec{libtool}). There is no separate library for 3m analogous to CGC's @filepath{libmzgc.a}.} @item{Under Windows, the stub library for use with Microsoft tools is @filepath{libmzsch3m@italic{x}.lib} (where @italic{x} represents the version number). This library identifies the bindings that are provided by @filepath{libmzsch3m@italic{x}.dll}. There is no separate library for 3m analogous to CGC's @filepath{libmzgc@italic{x}.lib}.} @item{Under Mac OS X, 3m dynamic libraries are provided by the @filepath{PLT_MzScheme} framework, just as for CGC, but as a version suffixed with @filepath{_3m}.} } For MzScheme3m, an embedding application must call @cpp{scheme_set_stack_base} with non-zero arguments. Furthermore, the first argument must be @cpp{&__gc_var_stack__}, where @cpp{__gc_var_stack__} is bound by a @cpp{MZ_GC_DECL_REG}. The simple embedding program from the previous section can be extended to work with either CGC or 3m, dependong on whether @cpp{MZ_PRECISE_GC} is specified on the compiler's command line: @verbatim[#<error_buf; scheme_current_thread->error_buf = &fresh; if (scheme_setjmp(scheme_error_buf)) { scheme_current_thread->error_buf = save; return -1; /* There was an error */ } else { v = scheme_eval_string(argv[i], e); scheme_display(v, curout); v = scheme_make_character('\n'); scheme_display(v, curout); /* read-eval-print loop, uses initial Scheme_Env: */ v = scheme_builtin_value("read-eval-print-loop"); scheme_apply(v, 0, NULL); scheme_current_thread->error_buf = save; } } MZ_GC_UNREG(); return 0; } EOS ] Strictly speaking, the @cpp{config} and @cpp{v} variables above need not be registered with the garbage collector, since their values are not needed across function calls that allocate. That is, the original example could have been left alone starting with the @cpp{scheme_base_env} call, except for the addition of @cpp{MZ_GC_UNREG}. The code is much easier to maintain, however, when all local variables are regsistered and when all temporary values are put into variables. @; ---------------------------------------------------------------------- @section{MzScheme and Threads} MzScheme implements threads for Scheme programs without aid from the operating system, so that MzScheme threads are cooperative from the perspective of C code. Under Unix, stand-alone MzScheme uses a single OS-implemented thread. Under Windows and Mac OS X, stand-alone MzScheme uses a few private OS-implemented threads for background tasks, but these OS-implemented threads are never exposed by the MzScheme API. In an embedding application, MzScheme can co-exist with additional OS-implemented threads, but the additional OS threads must not call any @cpp{scheme_} function. Only the OS thread that originally calls @cpp{scheme_basic_env} can call @cpp{scheme_} functions. (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.) When @cpp{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 @cpp{scheme_basic_env}. Thereafter, all calls to @cpp{scheme_} functions must originate from the new thread. See @secref["threads"] for more information about threads, including the possible effects of MzScheme's thread implementation on extension and embedding C code. @; ---------------------------------------------------------------------- @section[#:tag "im:unicode"]{MzScheme, Unicode, Characters, and Strings} A character in MzScheme is a Unicode code point. In C, a character value has type @cppi{mzchar}, which is an alias for @cpp{unsigned} --- which is, in turn, 4 bytes for a properly compiled MzScheme. Thus, a @cpp{mzchar*} string is effectively a UCS-4 string. Only a few MzScheme functions use @cpp{mzchar*}. Instead, most functions accept @cpp{char*} strings. When such byte strings are to be used as a character strings, they are interpreted as UTF-8 encodings. A plain ASCII string is always acceptable in such cases, since the UTF-8 encoding of an ASCII string is itself. See also @secref["im:strings"] and @secref["im:encodings"]. @; ---------------------------------------------------------------------- @section[#:tag "im:intsize"]{Integers} MzScheme expects to be compiled in a mode where @cppi{short} is a 16-bit integer, @cppi{int} is a 32-bit integer, and @cppi{long} has the same number of bits as @cpp{void*}. The @cppi{mzlonglong} type has 64 bits for compilers that support a 64-bit integer type, otherwise it is the same as @cpp{long}; thus, @cpp{mzlonglong} tends to match @cpp{long long}. The @cppi{umzlonglong} type is the unsigned version of @cpp{mzlonglong}.