Interapplication Communication Using SPAWN
This section explains how to use SPAWN to communicate with a child process (external program).
Communicating with a Child Process
In the previous chapter, the SPAWN procedure was used to start a child process and execute PV‑WAVE commands. The PV‑WAVE process waited until the child process was finished before continuing. The communication was one-way and only a single “transaction” was completed.
It is also possible to start a child process using SPAWN and continue the PV‑WAVE process without waiting for the child process to finish. To do this, PV‑WAVE attaches a bidirectional pipe to the standard input and output of the child process. This pipe appears in the PV‑WAVE process as a normal logical file unit. Once a process has been started in this way, the normal PV‑WAVE Input/Output facilities are used to communicate with it. The ability to use a child process in this manner allows you to solve specialized problems using other languages, and to take advantage of existing programs.
Starting the Child Process
In order to start such a process, the Unit keyword is used with SPAWN to specify a named variable into which the logical file unit number will be stored. Once the child process has done its work, the FREE_LUN procedure is used to close the pipe and delete the process.
When using a child process in this manner, it is important to understand the following points:
The EOF function always returns false when applied to a pipe. This means that it is not possible to use this function to know when the child process is finished. As a result, the child process must be written in such a way that the controlling PV‑WAVE procedure knows how much data to send and how much is coming back.
A UNIX pipe is a buffer maintained by the operating system. It has a fixed length, and can therefore become completely filled. When this happens, the operating system puts the process that is filling the pipe to sleep until the process at the other end consumes the buffered data. The use of a bidirectional pipe can therefore lead to deadlock situations in which both processes are waiting for each other. This can happen if the parent and child processes do not synchronize their reading and writing activities.
Most C programs use the input/output facilities provided by the Standard C Library
stdio. In situations where PV‑WAVE and the child process are carrying on a running dialog (as opposed to a single transaction), the normal buffering performed by
stdio on the output file can cause communications to hang. We recommend calling the
stdio function
setbuf() as the first statement of the child program to eliminate such buffering:
(void) setbuf(stdout, (char *) 0);
It is important that this statement occur before any output operation is executed, otherwise it will have no effect.
Example: Communicating with a Child Process Using SPAWN
This example C program returns a single-precision floating-point value, which is the average of the input values. The program also reads a long integer that tells how many data points to expect.
Since the amount of input and output for this program is explicitly known, and because it reads all of its input at the beginning and writes all of its results at the end, a deadlock situation as described in the previous section cannot occur.
note | In actual practice, such a trivial program would never be used from PV‑WAVE; it is simpler and more efficient to perform the calculation within PV‑WAVE. It does, however, serve to illustrate the method by which significant programs can be called from PV‑WAVE. |
This example assumes you have a C program, test_pipe.c, that accepts floating point values from its standard input and returns their average on the standard output. The code for such a C program is shown next.
#include <stdio.h>
/* System error number */
extern int errno;
/* System error messages */
extern char *sys_errlist[];
/* Length of sys_errlist*/
extern int sys_nerr;
main()
{
float *data, total = 0.0;
long i, n;
/* Make sure the output is not buffered */
setbuf(stdout, (char *) 0);
/* Find out how many points */
if (!fread(&n, sizeof(long), 1, stdin)) goto error;
/* Get memory for the array */
if (!(data = (float *) malloc((unsigned)
(n * sizeof(float))))) goto error;
/* Read the data */
if (!fread(data, sizeof(float), n, stdin)) goto error;
/* Calculate the average */
for (i=0; i < n; i++) total += data[i];
total /= (float) n;
/* Return the answer */
if (!fwrite(&total, sizeof(float), 1, stdout)) goto error;
return;
error:
fprintf(stderr, "test_pipe: %s\n", sys_errlist[errno]);
}
You can find the example file in:
$WAVE_DIR/demo/interapp/spawn/test_pipe.c
Using SPAWN to Access the C Program from PV-WAVE
The following PV‑WAVE statements use test_pipe to determine the average of the values 0 through 9:
; Start test_pipe. Use of the Noshell keyword is not necessary,
; but speeds up the startup process.
SPAWN, 'test_pipe', Unit=unit, /Noshell
; Send the number of points followed by the actual data.
WRITEU, unit, 10L, FINDGEN(10)
answer = 0.0
READU, unit, answer
PRINT, "Average = ", answer
; Close pipe, delete the child process, and deallocate the LUN.
FREE_LUN, unit
Executing these statements gives the result:
Average = 4.50000
This mechanism provides a simple way to augment PV‑WAVE with code written in other languages such as C or FORTRAN. In this case, however, it is not as efficient as writing the required operation entirely in PV‑WAVE. The actual cost depends primarily on the amount of data being transferred. For example, the above example can be performed entirely in PV‑WAVE using a simple statement like:
PRINT, 'Average = ', AVG(FINDGEN(10))
The PV‑WAVE calculation is always faster; however, the difference may only be significant when a large amount of data is transferred.
Version 2017.0
Copyright © 2017, Rogue Wave Software, Inc. All Rights Reserved.