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


1.7.1 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 a user to spawn any process (including multiple copies of XSB) and redirect its standard input and output to XSB streams. 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 stream) and talk to child processes through these pipes. All predicates in this section, except pipe_open/2 and fd2stream/2, must be imported from module shell. The predicates pipe_open/2 and fd2stream/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 stream identifiers for the process (leading to the subprocess standard input), from the process (from its standard output), and a stream 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),
     writeln(To,'Hello cat!'), flush_output(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 an atom and newline to the XSB stream To, which is bound to the standard input of the cat process (proc id 14328). The cat process then copies the input to its standard output. Since standard output of the cat process is redirected to the XSB stream From in the parent process, 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 flush_output/2. Flushing the output is extremely important here, because XSB I/O pipe (file) streams are buffered. Thus, cat might not see its input until the buffer is filled up, so the above clause might hang. flush_output/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 not to open one of the communication streams or to use one of the existing communication streams. 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 stream is not needed, it suffices to bind that stream to an atom. For instance,

| ?- spawn_process([cat, '-'], To, none, none, _),
     nl(To), writeln(To,'Hello cat!'), flush_output(To).


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

From = 4
S = \chapter{Library Utilities} \label{library_utilities}
In each case, only one of the streams is open. (Note that the shell command is specified as an atom rather than a list.) Finally, if both streams 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 stream variables in spawn_process is bound to an already existing file stream, then the subprocess will use that stream (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,_),
     write(To,'assert(p(1)).'), flush_output(To,_),
     write(To,'p(X), writeln(X).'), flush_output(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 stream and binds the variable XX to that output.

Finally, we should note that the stream 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:

| ?- open(test,write,Stream),
     spawn_process([cat, 'data'], none, FromCat1, none, _),
     spawn_process([sort], FromCat1,Stream, none, _).
Here, we first open file test. Then cat data is spawned. This process has the input and standard error stream blocked (as indicated by the atom none), and its output goes into stream FromCat1. Then we spawn another process, sort, which picks the output from the first process (since it uses the stream FromCat1 as its input) and sends its own output (the sorted version of data) to its output stream Stream. However, Stream 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: 1.7.1.0.1 Important notes about Up: 1.7 Script Writing Utilities Previous: 1.7 Script Writing Utilities   Contents   Index
Terrance Swift 2007-10-06