9.4.21 Embedding SWI-Prolog in other applications
With embedded Prolog we refer to the situation where the `main' program is not the Prolog application. Prolog is sometimes embedded in C, C++, Java or other languages to provide logic based services in a larger application. Embedding loads the Prolog engine as a library to the external language. Prolog itself only provides for embedding in the C language (compatible with C++). Embedding in Java is achieved using JPL using a C-glue between the Java and Prolog C interfaces.
The most simple embedded program is below. The interface function PL_initialise() must be called before any of the other SWI-Prolog foreign language functions described in this chapter, except for PL_initialise_hook(), PL_new_atom(), PL_new_functor() and PL_register_foreign(). PL_initialise() interprets all the command line arguments, except for the -t toplevel flag that is interpreted by PL_toplevel().
int
main(int argc, char **argv)
{
#ifdef READLINE /* Remove if you don't want readline */
PL_initialise_hook(install_readline);
#endif
if ( !PL_initialise(argc, argv) )
PL_halt(1);
PL_halt(PL_toplevel() ? 0 : 1);
}
- int PL_initialise(int argc, char **argv)
- Initialises the SWI-Prolog heap and stacks, restores the Prolog state,
loads the system and personal initialisation files, runs the at_initialization/1
hooks and finally runs the
-g goal hook.
Special consideration is required for
argv[0]. On Unix, this argument passes the part of the command line that is used to locate the executable. Prolog uses this to find the file holding the running executable. The Windows version uses this to find a module of the running executable. If the specified module cannot be found, it tries the modulelibpl.dll, containing the Prolog runtime kernel. In all these cases, the resulting file is used for two purposes:- See whether a Prolog saved state is appended to the file. If this is
the case, this state will be loaded instead of the default
boot.prcfile from the SWI-Prolog home directory. See also qsave_program/[1,2] and section 9.5. - Find the Prolog home directory. This process is described in detail in section 9.6.
PL_initialise() returns 1 if all initialisation succeeded and 0 otherwise.bugVarious fatal errors may cause PL_initialise() to call PL_halt(1), preventing it from returning at all.
In most cases, argc and argv will be passed from the main program. It is allowed to create your own argument vector, provided
argv[0]is constructed according to the rules above. For example:int main(int argc, char **argv) { char *av[10]; int ac = 0; av[ac++] = argv[0]; av[ac++] = "-x"; av[ac++] = "mystate"; av[ac] = NULL; if ( !PL_initialise(ac, av) ) PL_halt(1); ... }Please note that the passed argument vector may be referred from Prolog at any time and should therefore be valid as long as the Prolog engine is used.
A good setup in Windows is to add SWI-Prolog's
bindirectory to yourPATHand either pass a module holding a saved state, or"libpl.dll"asargv[0]. If the Prolog state is attached to a DLL (see the -dll option of swipl-ld), pass the name of this DLL. - See whether a Prolog saved state is appended to the file. If this is
the case, this state will be loaded instead of the default
- int PL_is_initialised(int *argc, char ***argv)
- Test whether the Prolog engine is already initialised. Returns
FALSEif Prolog is not initialised andTRUEotherwise. If the engine is initialised and argc is notNULL, the argument count used with PL_initialise() is stored in argc. Same for the argument vector argv. - void PL_install_readline()
- Installs the GNU readline line editor. Embedded applications that do not use the Prolog top level should normally delete this line, shrinking the Prolog kernel significantly. Note that the Windows version does not use GNU readline.
- int PL_toplevel()
- Runs the goal of the -t toplevel switch (default prolog/0) and returns 1 if successful, 0 otherwise.
- int PL_cleanup(int status)
- This function performs the reverse of PL_initialise().
It runs the
PL_on_halt()
and at_halt/1
handlers, closes all streams (except for the `standard I/O' streams
which are flushed only), deallocates all memory and restores all signal
handlers. The status argument is passed to the various
termination hooks and indicates the exit-status.
The function returns
TRUEif successful andFALSEotherwise. Currently,FALSEis returned when an attempt is made to call PL_cleanup() recursively or if PL_cleanup() is not called from the main thread.In theory, this function allows deleting and restarting the Prolog system in the same process. In practice, SWI-Prolog's cleanup process is far from complete, and trying to revive the system using PL_initialise() will leak memory in the best case. It can also crash the appliction.
In this state, there is little practical use for this function. If you want to use Prolog temporarily, consider running it in a separate process. If you want to be able to reset Prolog, your options are (again) a separate process, modules or threads.
- void PL_cleanup_fork()
- Close file descriptors associated to Prolog streams except for 0,1 and 2.
Stop intervaltimer that may be running on behalf of profile/1.
The call is intended to be used in combination with fork():
if ( (pid=fork()) == 0 ) { PL_cleanup_fork(); <some exec variation> }The call behaves the same on Windows, though there is probably no meaningful application.
- int PL_halt(int status)
- Clean up the Prolog environment using PL_cleanup()
and call exit() with the status argument. As PL_cleanup()
can only be called from the main thread, this function returns
FALSEwhen called from a thread other than the main one.bugEventually it may become possible to call PL_halt() from any thread.
9.4.21.1 Threading, Signals and embedded Prolog
This section applies to Unix-based environments that have signals or multithreading. The Windows version is compiled for multithreading, and Windows lacks proper signals.
We can distinguish two classes of embedded executables. There are small C/C++ programs that act as an interfacing layer around Prolog. Most of these programs can be replaced using the normal Prolog executable extended with a dynamically loaded foreign extension and in most cases this is the preferred route. In other cases, Prolog is embedded in a complex application that---like Prolog---wants to control the process environment. A good example is Java. Embedding Prolog is generally the only way to get these environments together in one process image. Java applications, however, are by nature multithreaded and appear to do signal handling (software interrupts).
On Unix systems, SWI-Prolog uses three signals:
- SIGUSR1
- is used to sychronise atom and clause garbage collection. The handler is installed at the start of garbage collection and reverted to the old setting after completion.
- SIGUSR2
- has an empty signal handler. This signal is sent to a thread after
sending a thread-signal (see
thread_signal/2).
It causes blocking system calls to return with
EINTR, which gives them the opportunity to react to thread-signals. - SIGINT
- is used by the top level to activate the tracer (typically bound to control-C). The first control-C posts a request for starting the tracer in a safe, synchronous fashion. If control-C is hit again before the safe route is executed, it prompts the user whether or not a forced interrupt is desired.
The --nosignals option can be used to inhibit
processing of SIGINT. The other signals are vital for the
functioning of SWI-Prolog. If they conflict with other applications,
signal handling of either component must be modified. The SWI-Prolog
signals are defined in
pl-thread.h of the source distribution.