[ 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 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), %% We aren't going to read from child, so let's close the pipe coming %% from it -- we don't want to run out of file descriptors!!! fd2ioport(RP,RF), file_close(RF), %% 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/3 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.
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.