next up previous contents index
Next: Notes: Up: Communication with Subprocesses Previous: Communication with Subprocesses   Contents   Index

Important notes about spawned processes:

  1. Asynchronous processes spawned by XSB do not disappear (at least on Unix) when they terminate, unless the XSB program executes a wait on them (see process_control below). Instead, such processes become defunct zombies (in Unix terminology); they do not do anything, but consume resources (such as file descriptors). So, when a subprocess is known to terminate, it must be waited on.

  2. The XSB parent process must know how to terminate the asynchronous subprocesses it spawns. The drastic way is to kill it (see process_control below). Sometimes a subprocess might terminate by itself (e.g., having finished reading a file). In other cases, the parent and the child programs must agree on a protocol by which the parent can tell the child to exit. The programs in the XSB subdirectory examples/subprocess illustrate this idea. If the child subprocess is another XSB process, then it can be terminated by sending the atom end_of_file or halt to the standard input of the child. (For this to work, the child XSB must waiting at the prompt).
  3. It is very important to not forget to close the I/O ports that the parent uses to communicate with the child. These are the ports that are provided in arguments 2,3,4 of spawn_process. The reason is that the child might terminate, but these ports will remain open, since they belong to the parent process. As a result, the parent will own defunct I/O ports and might eventually run out of file descriptors.

process_status(+Pid,-Status)

This predicate always succeeds. Given a process id, it binds the second argument (which must be an unbound variable) to one of the following atoms: running, stopped, exited_normaly, exited_abnormally, aborted, invalid, and unknown. The invalid status is given to processes that never existed or that are not children of the parent XSB process. The unknown status is assigned when none of the other statuses can be assigned.

Note: process status (other than running) is system dependent. Windows does not seem to support stopped and aborted. Also, processes killed using the process_control predicate (described next) are often marked as invalid rather than exited, because Windows seems to lose all information about such processes. Process status might be inaccurate in some Unix systems as well, if the process has terminated and wait() has been executed on that process.

process_control(+Pid,+Operation)

Perform a process control operation on the process with the given Pid. Currently, the only supported operations are kill (an atom) and wait(Code) (a term). The former causes the process to exit unconditionally, and the latter waits for process completion. When the process exits, Code is bound to the process exit code. The code for normal termination is 0.

This predicate succeeds, if the operation was performed successfully. Otherwise, it fails. The wait operation fails if the process specified in Pid does not exist or is not a child of the parent XSB process.

The kill operation might fail, if the process to be killed does not exist or if the parent XSB process does not have the permission to terminate that process. Unix and Windows have different ideas as to what these permissions are. See kill(2) for Unix and TerminateProcess for Windows.

Note: under Windows, the programmer's manual warns of dire consequences if one kills a process that has DLLs attached to it.

get_process_table(-ProcessList)

This predicate is imported from module shell. It binds ProcessList to the list of terms, each describing one of the active XSB subprocesses (created via spawn_process/5). Each term has the form:
process(Pid,ToStream,FromStream,StderrStream,CommandLine).
The first argument in the term is the process id of the corresponding process, the next three arguments describe the three standard ports of the process, and the lat is an atom that shows the command line used to invoke the process. This predicate always succeeds.

shell(+CmdSpec,-StreamToProc, -StreamFromProc, -ProcStderr, -ErrorCode)

The arguments of this predicate are similar to those of spawn_process, except for the following: (1) The first argument is an atom or a list of atoms, like in spawn_process. However, if it is a list of atoms, then the resulting shell command is obtained by string concatenation. This is different from spawn_process where each member of the list must represent an argument to the program being invoked (and which must be the first member of that list). (2) The last argument is the error code returned by the shell command and not a process id. The code -1 and 127 mean that the shell command failed.

The shell/5 predicate is similar to spawn_process in that it spawns another process and can capture that process' input and output ports.

The important difference, however, is that XSB will ways until the process spawned by shell/5 terminates. In contrast, the process spawned by spawn_process will run concurrently with XSB. In this latter case, XSB must explicitly synchronize with the spawned subprocess using the predicate process_control/2 (using the wait operation), as described earlier.

The fact that XSB must wait until shell/5 finishes has a very important implication: the amount of data the can be sent to and from the shell command is limited (1K is probably safe). This is because the shell command communicates with XSB via pipes, which have limited capacity. So, if the pipe is filled, XSB will hang waiting for shell/5 to finish and shel/5 will wait for XSB to consume data from the pipe. Thus, use spawn_process/5 for any kind of significant data exchange between external processes and XSB.

Another difference between these two forms of spawning subprocesses is that CmdSpec in shell/5 can represent any shell statement, including those that have pipes and I/O redirection. In contrast, spawn_process only allows command of the form ``program args''. For instance,

| ?- file_open(test,w,IOport),
     shell('cat | sort > data', IOport, none, none, ErrCode)
As seen from this example, the same rules for blocking I/O streams apply to shell/5. Finally, we should note that the already familiar standard predicates shell/1 and shell/2 are implemented using shell/5.


next up previous contents index
Next: Notes: Up: Communication with Subprocesses Previous: Communication with Subprocesses   Contents   Index
Luis Fernando P. de Castro 2003-06-27