next up previous contents index
Next: Important notes about spawned Up: Library Utilities Previous: Script Writing Utilities   Contents   Index

Communication with Subprocesses

In the previous section, we have seen several predicates that allow XSB to create other processes. However, these predicates offer only a very limited way to communicate with these processes. The predicate spawn_process/5 and friends come to the rescue. It allows to spawn any process (including multiple copies of XSB) and redirect its standard input and output to XSB I/O ports. XSB can then write to the process and read from it. The section of socket I/O describes yet another mode of interprocess communication.

In addition, the predicate pipe_open/2 described in this section lets one create any number of pipes (that do not need to be connected to the standard I/O port) and talk to child processes through these pipes.

All predicates in this section, except pipe_open/2 and fd2ioport/2, must be imported from module shell. The predicates pipe_open/2 and fd2ioport/2 must be imported from file_io.

spawn_process(+CmdSpec,-StreamToProc,-StreamFromProc,-ProcStderrStream,-ProcId)

Spawn a new process specified by CmdSpec. CmdSpec must be either a single atom or a list of atoms. If it is an atom, then it must represent a shell command. If it is a list, the first member of the list must be the name of the program to run and the other elements must be arguments to the program. Program name must be specified in such a way as to make sure the OS can find it using the contents of the environment variable PATH. Also note that pipes, I/O redirection and such are not allowed in command specification. That is, CmdSpec must represent a single command. (But read about process plumbing below and about the related predicate shell/5.)

The next three parameters of spawn_process are XSB I/O ports to the process (leading to the subprocess standard input), from the process (from its standard output), and a port capturing the subprocess standard error output. The last parameter is the system process id.

Here is a simple example of how it works.

| ?- import file_flush/2, file_read_line_atom/2 from file_io.
| ?- import file_nl/1 , file_write/2 from xsb_writ.  

| ?- spawn_process([cat, '-'], To, From, Stderr, Pid),
     file_write(To,'Hello cat!'), file_nl(To), file_flush(To,_),
     file_read_line_atom(From,Y).

To = 3
From = 4
Stderr = 5
Pid = 14328
Y = Hello cat!

yes

Here we created a new process, which runs the ``cat'' program with argument ``-''. This forces cat to read from standard input and write to standard output. The next line writes a newline-terminated string to the XSB port To the, which is bound to the standard input of the cat process. The process then copies the input to the standard output. Since standard output of the process is redirected to the XSB port From, the last line in our program is able to read it and return in the variable Y.

Note that in the second line we used file_flush/2. Flushing the output is extremely important here, because XSB I/O ports are buffered. Thus, cat might not see its input until the buffer is filled up, so the above clause might hang. file_flush/2 makes sure that the input is immediately available to the subprocess.

In addition to the above general schema, the user can tell spawn_process/5 to not open one of the communication ports or to use one of the existing communication ports. This is useful when you do not expect to write or read to/from the subprocess or when one process wants to write to another (see the process plumbing example below).

To tell that a certain port is not needed, it suffices to bind that port to an atom. For instance,

| ?- spawn_process([cat, '-'], To, none, none, _),
     file_nl(To), file_write(To,'Hello cat!'), file_nl(To), file_flush(To,_).


To = 3,
Hello cat!
reads from XSB and copies the result to standard output. Likewise,
| ?- spawn_process('cat test', none, From, none, _),
     file_read_line_atom(From, S).

From = 4
S = The first line of file `test'
In each case, only one of the ports is open. (Note that the shell command is specified as an atom rather than a list.) Finally, if both ports are suppressed, then spawn_process reduces to the usual shell/1 call (in fact, this is how shell/1 is implemented):
| ?- spawn_process([pwd], none, none).

/usr/local/foo/bar
On the other hand, if any one of the three port variables in spawn_process is bound to an already existing file port, then the subprocess will use that port (see the process plumbing example below).

One of the uses of XSB subprocesses is to create XSB servers that spawn subprocesses and control them. A spawned subprocess can be another XSB process. The following example shows one XSB process spawning another, sending it a goal to evaluate and obtaining the result:

| ?- spawn_process([xsb], To, From,Err,_),
     file_write(To,'assert(p(1)).'), file_nl(To), file_flush(To,_),
     file_write(To,'p(X), writeln(X).'), file_nl(To), file_flush(To,_), 
     file_read_line_atom(From,XX).  

XX = 1

yes
| ?-
Here the parent XSB process sends ``assert(p(1)).'' and then ``p(X), writeln(X).'' to the spawned XSB subprocess. The latter evaluates the goal and prints (via ``writeln(X)'') to its standard output. The main process reads it through the From port and binds the variable XX to that output.

Finally, we should note that the port variables in the spawn_process predicate can be used to do process plumbing, i.e., redirect output of one subprocess into the input of another. Here is an example:

| ?- file_open(test,w,IOport),
     spawn_process([cat, 'data'], none, FromCat1, none, _),
     spawn_process([sort], FromCat1, IOport, none, _).
Here, we first open file test. Then cat data is spawned. This process has the input and standard error ports blocked (as indicated by the atom none), and its output goes into port FromCat1. Then we spawn another process, sort, which picks the output from the first process (since it uses the port FromCat1 as its input) and sends its own output (the sorted version of data) to its output port IOport. However, IOport has already been open for output into the file test. Thus, the overall result of the above clause is tantamount to the following shell command:
        cat data | sort > test



Subsections
next up previous contents index
Next: Important notes about spawned Up: Library Utilities Previous: Script Writing Utilities   Contents   Index
Luis Fernando P. de Castro 2003-06-27