Node:Foreign Resources and Multiple SICStus Run-Times, Next:, Previous:Multiple SICStus Run-Times in C, Up:Multiple SICStus Run-Times



Foreign Resources and Multiple SICStus Run-Times

Foreign resources access the SICStus C API in the same way as an embedding application, that is, through a dispatch vector. As for applications, the default and backward compatible mode is to only support a single SICStus run-time. An alternative mode makes it possible for a foreign resource to be shared between several SICStus run-times in the same process.

Unless otherwise noted, this section documents the behavior when using dynamically linked foreign resources. That is, shared objects (.so-files) on UNIX, dynamic libraries (DLLs) on Windows.

Foreign Resources Supporting Only One SICStus Run-Time

A process will only contain one instance of the code and data of a (dynamic) foreign resource even if the foreign resource is loaded and used from more than one SICStus run-time.

This presents a problem in the likely event that the foreign resource maintains some state, e.g. global variables, between invocations of functions in the foreign resource. The global state will probably need to be separate between SICStus run-times. Requiring a foreign resource to maintain its global state on a per SICStus run-time basis would be an incompatible change. Instead, by default, only the first SICStus run-time that loads a foreign resource will be allowed to use it. If a subsequent SICStus run-time (in the same process) tries to load the foreign resource then an error will be reported to the second SICStus run-time.

When splfr builds a foreign resource, it will also generate glue code. When the foreign resource is loaded, the glue code will set up a global variable pointing to the dispatch vector used in the foreign resource to access the SICStus API. This is similar to how an embedding application accesses the SICStus API.

The glue code will also detect if a subsequent SICStus run-time in the same process tries to initialize the foreign resource. In this case, an error will be reported.

This means that pre 3.9 foreign code should only need to be rebuilt with splfr to work with the latest version of SICStus. However, a recommended change is that all C files of a foreign resource include the header file generated by splfr. Inclusion of this generated header file may become mandatory in a future release. See The Foreign Resource Linker.

Foreign Resources Supporting Multiple SICStus run-times

A foreign resource that wants to be shared between several SICStus run-times must somehow know which SICStus run-time is calling it so that it can make callbacks using the SICStus API into the right SICStus run-time. In addition, the foreign resource may have global variables that should have different values depending on which SICStus run-time is calling the foreign resource.

A header file is generated by splfr when it builds a foreign resource (before any C code is compiled). This header file provides prototypes for any foreign-declared function, but it also provides other things needed for multiple SICStus run-time support. This header file must be included by any C file that contains code that either calls any SICStus API function or that contains any of the functions called by SICStus. See The Foreign Resource Linker.

Simplified Support for Multiple SICStus Run-Times

To make it simpler to convert old foreign resources, there is an intermediate level of support for multiple SICStus run-times. This level of support makes it possible for several SICStus run-times to call the foreign resource, but a mutual exclusion lock ensures that only one SICStus run-time at a time can execute code in the foreign resource. That is, the mutex is locked upon entry to any function in the foreign resource and unlocked when the function returns. This makes it possible to use a global variable to hold the SICStus dispatch vector, in much the same way as is done when only a single SICStus run-time is supported. In addition, a special hook function in the foreign resource will be called every time the foreign resource is entered. This hook function can then make arrangements to ensure that any global variables are set up as appropriate.

To build a foreign resource in this way, use splfr --exclusive-access. In addition to including the generated header file, your code needs to define the context switch function. If the resource is named resname then the context switch hook should look like:

void sp_context_switch_hook_resname(int entering)

The context switch hook will be called with the SICStus API dispatch vector already set up, so calling any SICStus API function from the context switch hook will work as expected. The argument entering will be non-zero when a SICStus run-time is about to call a function in the foreign resource. The hook will be called with entering zero when the foreign function is about to return to SICStus.

It is possible to specify a name for the context switch hook with the splfr option --context-hook=name. If you do not require a context switch hook you can specify the splfr option --no-context-hook.

Due to the use of mutual exclusion lock to protect the foreign resource, there is a remote possibility of dead-lock. This would happen if the foreign resource calls back to SICStus and then passes control to a different SICStus run-time in the same thread which then calls the foreign resource. For this reason it is best to avoid --exclusive-access for foreign resources that makes call-backs into Prolog.

The new SICStus API function SP_foreign_stash() provides access to a location where the foreign resource can store anything that is specific to the calling SICStus run-time. The location is specific to each foreign resource and each SICStus run-time. See Miscellaneous C API Functions.

C code compiled by splfr --exclusive-access will have the C pre-processor macro SP_SINGLE_THREADED defined to a non-zero value.

Some of the foreign resources in the SICStus library use this technique; see for instance library(system).

Full Support for Multiple SICStus Run-Times

To fully support multiple SICStus run-times, a foreign resource should be built with splfr --multi-sp-aware.

C code compiled by splfr --multi-sp-aware will have the C pre-processor macro MULTI_SP_AWARE defined to a non-zero value.

Full support for multiple SICStus run-times means that more than one SICStus run-time can execute code in the foreign resource at the same time. This rules out the option to use any global variables for information that should be specific to each SICStus run-time. In particular, the SICStus dispatch vector cannot be stored in a global variable. Instead, the SICStus dispatch vector is passed as an extra first argument to each foreign function.

To ensure some degree of link time type checking, the name of each foreign function will be changed (using #define in the generated header file).

The extra argument is used in the same way as when using multiple SICStus run-times from an embedding application. It must be passed on to any function that needs access to the SICStus API.

To simplify the handling of this extra argument, several macros are defined so that the same foreign resource code can be compiled both with and without support for multiple SICStus run-times.

SPAPI_ARG0
SPAPI_ARG
SPAPI_ARG_PROTO_DECL0
SPAPI_ARG_PROTO_DECL

Their use is easiest to explain with an example. Suppose the original foreign code looked like:

static int f1(void)
{
        some SICStus API calls
}

static int f2(SP_term_ref t, int x)
{
        some SICStus API calls
}

/* :- foreign(foreign_fun, c, foreign_pred(+integer)). */
void foreign_fun(long x)
{
  ... some SICStus API calls ...
  f1();
  ...
  f2(SP_new_term_ref(), 42);
  ...
}

Assuming no global variables are used, the following change will ensure that the SICStus API dispatch vector is passed around to all functions:

static int f1(SPAPI_ARG_PROTO_DECL0) // _DECL<ZERO> for no-arg functions
{
        some SICStus API calls
}

static int f2(SPAPI_ARG_PROTO_DECL SP_term_ref t, int x) // Note: no comma
{
        some SICStus API calls
}

/* :- foreign(foreign_fun, c, foreign_pred([-integer])). */
void foreign_fun(SPAPI_ARG_PROTO_DECL long x) // Note: no comma
{
  ... some SICStus API calls ...
  f1(SPAPI_ARG0);               // ARG<ZERO> for no-arg functions
  ...
  f2(SPAPI_ARG SP_new_term_ref(), 42);       // Note: no comma
  ...
}

If MULTI_SP_AWARE is not defined, i.e. --multi-sp-aware is not specified to splfr, then all these macros expand to nothing, except SPAPI_ARG_PROTO_DECL0 which will expand to void.

You can use SP_foreign_stash() to get access to a location, initially set to NULL, where the foreign resource can store a void*. Typically this would be a pointer to a C struct that holds all information that need to be stored in global variables. This struct can be allocated and initialized by the foreign resource initialization function. It should be deallocated by the foreign resource deinit function. See Miscellaneous C API Functions, for details.

Simple stand-alone examples will be provided in the final version of 3.9. Until then, for an example of code that fully supports multiple SICStus run-times. see library/jasper/jasper.c. For an example which hides the passing of the extra argument by using the C pre-processor, see the files in library/clpfd/.