Go to the first, previous, next, last section, table of contents.

Active modules (high-level distributed execution)

Author(s): Manuel Hermenegildo, Daniel Cabeza.

Version: 1.10#6 (2004/8/7, 21:46:39 CEST)

Version of last change: 1.9#2 (2002/5/23, 17:48:34 CEST)

Active modules [CH95] provide a high-level model of inter-process communication and distributed execution (note that this is also possible using Ciao's communication and concurrency primitives, such as sockets, concurrent predicates, etc., but at a lower level of abstraction). An active module (or an active object) is an ordinary module to which computational resources are attached, and which resides at a given location on the network. Compiling an active module produces an executable which, when running, acts as a server for a number of predicates: the predicates exported by the module. Predicates exported by an active module can be accessed by a program on the network by simply "using" the module, which then imports such "remote predicates." The process of "using" an active module does not involve transferring any code, but rather setting up things so that calls in the module using the active module are executed as remote procedure calls to the active module. This occurs in the same way independently of whether the active module and the using module are in the same machine or in different machines across the network.

Except for having to compile it in a special way (see below), an active module is identical from the programmer point of view to an ordinary module. A program using an active module imports it and uses it in the same way as any other module, except that it uses " use_active_module" rather than " use_module" (see below). Also, an active module has an address (network address) which must be known in order to use it. In order to use an active module it is necessary to know its address: different "protocols" are provided for this purpose (see below).


From the implementation point of view, active modules are essentially daemons: executables which are started as independent processes at the operating system level. Communication with active modules is implemented using sockets (thus, the address of an active module is an IP socket address in a particular machine). Requests to execute goals in the module are sent through the socket by remote programs. When such a request arrives, the process running the active module takes it and executes it, returning through the socket the computed answers. These results are then taken and used by the remote processes. Backtracking over such remote calls works as usual and transparently. The only limitation (this may change in the future, but it is currently done for efficiency reasons) is that all alternative answers are precomputed (and cached) upon the first call to an active module and thus an active module should not export a predicate which has an infinite number of answers.

The first thing to do is to select a method whereby the client(s) (the module(s) that will use the active module) can find out in which machine/port (IP address/socket number) the server (i.e., the active module) will be listening once started, i.e., a "protocol" to communicate with the active module. The easiest way to do this is to make use of the redezvous methods which are provided in the Ciao distribution in the library/actmods directory; currently, tmpbased..., filebased..., and webbased....

The first one is based on saving the IP address and socket number of the server in a file in a predefined directory (generally /tmp, but this can be changed by changing tmpbased_common.pl).

The second one is similar but saves the info in the directory in which the server is started (as <module_name>.addr), or in the directory that a .addr file, if it exists, specifies. The clients must be started in the same directory (or have access to a file .addr specifying the same directory). However, they can be started in different machines, provided this directory is shared (e.g., by NFS or Samba), or the file can be moved to an appropriate directory on a different machine --provided the full path is the same.

The third one is based on a name server for active modules. When an active module is started, it communicates its address to the name server. When the client of the active module wants to communicate with it, it asks the name server the active module address. This is all done transparently to the user. The name server must be running when the active module is started (and, of course, when the application using it is executed). The location of the name server for an application must be specified in an application file named webbased_common.pl (see below).

These rendezvous methods are encoded in two modules: a first one, called ...publish.pl, is used by the server to publish its info. The second one, called ...locate.pl, is used by the client(s) to locate the server info. For efficiency, the client methods maintain a cache of addresses, so that the server information only needs to be read from the file system the first time the active module is accessed.

Active modules are compiled using the -a option of the Ciao compiler (this can also be done from the interactive top-level shell using make_actmod/2). For example, issuing the following command:

  ciaoc -a 'actmods/filebased_publish' simple_server

compiles the simple server example that comes with the distribution (in the actmods/example directory). The simple_client_with_main example (in the same directory) can be compiled as usual:

  ciaoc simple_client_with_main

Note that the client uses the actmods package, specifies the rendezvous method by importing library('actmods/filebased_locate'), and explicitely imports the "remote" predicates (implicit imports will not work). Each module using the actmods package should only use one of the rendezvous methods.

Now, if the server is running (e.g., simple_server & in Un*x or double-clicking on it in Win32) when the client is executed it will connect with the server to access the predicate(s) that it imports from it.

A simpler even client simple_client.pl can be loaded into the top level and its predicates called as usual (and they will connect with the server if it is running).

Active module name servers

An application using a name server for active modules must have a file named webbased_common.pl that specifies where the name server resides. It must have the URL and the path which corresponds to that URL in the file system of the server machine (the one that hosts the URL) of the file that will hold the name server address.

The current distribution provides a file webbased_common.pl that can be used (after proper setting of its contents) for a server of active modules for a whole installation. Alternatively, particular servers for each application can be set up (see below).

The current distribution also provides a module that can be used as name server by any application. It is in file examples/webbased_server/webbased_server.pl.

To set up a name server edit webbased_common.pl to change its contents appropriately as described above (URL and corresponding complete file path). Then recompile this library module:

    ciaoc -c webbased_common

The name server has to be compiled as an active module itself:

    ciaoc -a actmods/webserver_publish webbased_server

It has to be started in the server machine before the application and its active modules are compiled.

Alternatively, you can copy webbased_common.pl and use it to set up name servers for particular applications. Currently, this is a bit complicated. You have to ensure that the name server, the application program, and all its active modules are compiled and executed with the same webbased_common.pl module. One way to do this is to create a subdirectory actmods under the directory of your application, copy webbased_common.pl to it, modify it, and then compile the name server, the application program, and its active modules using a library path that guarantees that your actmods directory is located by the compiler before the standard Ciao library. The same applies for when running all of them if the library loading is dynamic.

One way to do the above is using the -u compiler option. Assume the following file:

     :- module(paths,[],[]).
     :- multifile library_directory/1.
     :- dynamic library_directory/1.
     :- initialization(asserta_fact(
	library_directory('/root/path/to/my/particular/application') )).

then you have file webbased_common.pl in a subdirectory actmods of the above cited path. You have to compile the name server, the active modules, and the rest of the application with:

    ciaoc -u paths -s ...

to use your particular webbased_common.pl and to make executables statically link libraries. If they are dynamic, then you have to provide for the above library_directory path to be set up upon execution. This can be done, for example, by including module paths into your executables.

Addresses of active modules are saved by the name server in a subdirectory webbased_db of the directory where you start it --see examples/webbased_server/webbased_db/webbased_server). This allows to restart the server right away if it dies (since it saves its state). This directory should be cleaned up regularly of addresses of active modules which are no more active. To do this, stop the server --by killing it (its pid is in PATH/FILE), and restart it after cleaning up the files in the above mentioned directory.

Active modules as agents

It is rather easy to turn Ciao active modules into agents for some kind of applications. The directory examples/agents contains a (hopefully) self-explanatory example.

Usage and interface (actmods)

Documentation on new declarations (actmods)

DECLARATION: use_active_module/2:

Usage: :- use_active_module(AModule, Imports).

Known bugs and planned improvements (actmods)

Go to the first, previous, next, last section, table of contents.