next up previous contents index
Next: Socket I/O Up: Communication with Subprocesses Previous: Important notes about spawned   Contents   Index

Notes:

  1. With shell/5, you do not have to worry about terminating child processes: XSB waits until the child exits automatically. However, since communication pipes have limited capacity, this method can be used only for exchanging small amounts of information between parent and child.
  2. The earlier remark about the need to close I/O streams to the child does apply.

[pipe_open(-ReadPipe, -WritePipe)]
Open a new pipe and return the read end and the write end of that pipe. If the operation fails, both ReadPipe and WritePipe are bound to negative numbers.

The pipes returned by the pipe_open/2 predicate are small integers that represent file descriptors used by the underlying OS. They are not XSB I/O ports, and they cannot be used for I/O directly. To use them, one must call the fd2ioport/2 predicate, as described next.1.2[fd2ioport(+Pipe, -IOport)]
Take a pipe and convert it to an XSB I/O port that can be used for I/O. This predicate is needed because pipes must be associated with XSB I/O ports before any I/O can be done on them by an XSB program.

The best way to illustrate how one can create a new pipe to a child (even if the child has been created earlier) is to show an example. Consider two programs, parent.P and child.P. The parent copy of XSB consults parent.P, which does the following: First, it creates a pipe and spawns a copy of XSB. Then it tells the child copy of XSB to assert the fact pipe(RP), where RP is a number representing the read part of the pipe. Next, the parent XSB tells the child XSB to consult the program child.P. Finally, it sends the message Hello!.

The child.P program gets the pipe from predicate pipe/1 (note that the parent tells the child XSB to first assert pipe(RP) and only then to consult the child.P file). After that, the child reads a message from the pipe and prints it to its standard output. Both programs are shown below:

%% parent.p      
:- import pipe_open/2, fd2ioport/2, fmt_write/3, file_flush/2 from file_io.
%% Create the pipe and pass it to the child process
?- pipe_open(RP,WP),
   %% WF is now the XSB I/O port bound to the write part of the pipe
   fd2ioport(WP,WF),
   %% ProcInput becomes the XSB stream leading directly to the child's stdin
   spawn_process(xsb, ProcInput, block, block, Process),
   %% Tell the child where the reading part of the pipe is
   fmt_write(ProcInput, "assert(pipe(%d)).\n", arg(RP)),
   fmt_write(ProcInput, "[child].\n", _),
   file_flush(ProcInput, _),
   %% Pass a message through the pipe
   fmt_write(WF, "Hello!\n", _),
   file_flush(WF, _),
   fmt_write(ProcInput, "end_of_file.\n",_), % send end_of_file atom to child
   file_flush(ProcInput, _),
   %% wait for child (so as to not leave zombies around; 
   %% zombies quit when the parent finishes, but they consume resources)
   process_control(Process, wait),
   %% Close the ports used to commuicate with the process
   %% Otherwise, the parent might run out of file descriptors 
   %% (if many processes were spawned)
   file_close(ProcInput), file_close(WF).
%% child.P
:- import fd2ioport/2 from file_io.
:- import file_read_line_atom/2 from file_io.
:- dynamic pipe/1.
?- pipe(P), fd2ioport(P,F),
   %% Acknowledge receipt of the pipe
   fmt_write("\nPipe %d received\n", arg(P)),
   %% Get a message from the parent and print it to stdout
   file_read_line_atom(F, Line), write('Message was: '), writeln(Line).
This produces the following output:
| ?- [parent].                    <- parent XSB consults parent.P
[parent loaded]
yes
| ?- [xsb_configuration loaded]   <- parent.P spawns a child copy of XSB
[sysinitrc loaded]                   Here we see the startup messages of
[packaging loaded]                   the child copy
XSB Version 2.0 (Gouden Carolus) of June 27, 1999
[i686-pc-linux-gnu; mode: optimal; engine: slg-wam; scheduling: batched]
| ?- 
yes
| ?- [Compiling ./child]          <- The child copy of received the pipe from
[child compiled, cpu time used: 0.1300 seconds]     the parent and then the
[child loaded]                                      request to consult child.P
Pipe 15 received                  <- child.P acknowledges receipt of the pipe
Message was: Hello!               <- child.P gets the message and prints it
yes

Observe that the parent process is very careful about making sure that the child terminates and also about closing the I/O ports after they are no longer needed.

Finally, we should note that this mechanism can be used to communicate through pipes with non-XSB processes as well. Indeed, an XSB process can create a pipe using pipe_open (before spawning a child process), pass one end of the pipe to a child process (which can be a C program), and use fd2ioport to convert the other end of the pipe to an XSB file. The C program, of course, does not need fd2ioport, since it can use the pipe file handle directly. Likewise, a C program can spawn off an XSB process and pass it one end of a pipe. The XSB child-process can then convert this pipe fd to a file using fd2ioport and then talk to the paren C program.

[sys_exit(-ExitCode)]
This predicate causes XSB subprocess to exit unconditionally with the exit code ExitCode. Normally 0 is considered to indicate normal termination, while other exit codes are used to report various degrees of abnormality.


next up previous contents index
Next: Socket I/O Up: Communication with Subprocesses Previous: Important notes about spawned   Contents   Index
Luis Fernando P. de Castro 2003-06-27