What is the Named Pipe Option?

The named pipe option can be used to display data in real-time. That is, with the named pipe option you can use xmgr to display data immediately as you generate them by your number-crunching program. In contrast with the simple pipe option, xmgr's graphic user interface remains responsive to your actions while displaying the data.

What is a Named Pipe Anyway?

A simple pipe is used, for example, by the command

    grep searchfor file | more

The "|" symbol is the pipe, i.e. the output of grep is mounted to one end of the pipe, and the input of more is mounted to the other end of the pipe. Everything that grep writes to its standard output goes through the pipe, and more sees this on its standard input. Another name for a pipe is FIFO (First In First Out). That means that things arrive at the end of the pipe in the same order as you put them in (in contrast with a stack, where you'll see first what you put in last).

A named pipe is essentially the same, except that (you guessed it) the pipe gets a file name. Before we can use a named pipe, we have to create it. That can be done using

    mkfifo /tmp/mynamedpipe

from the shell prompt, or using the C function

    #include <sys/stat.h>
    mkfifo ("/tmp/mypipename", 0600);

where 0600 sets the access permissions for the named pipe.

After having created the named pipe, one program can open that file name for writing (as you would open any ordinary file), and another program can open the file for reading. Here is a simple example:

    mkfifo /tmp/testpipe
    echo "This is a test" > /tmp/testpipe &
    cat /tmp/testpipe
    rm /tmp/testpipe

You can use the the C functions "open" or "fopen" to mount a process to the pipe.

The named pipe behaves very much like an ordinary file, except that

  1. only one process can open the named pipe for writing, and only one process can open the pipe for reading (at the same time),
  2. nothing is written to the disk but rather into some memory buffer,
  3. if the listening end closes the pipe while the talking end has the pipe still open for writing, the talking end gets a signal SIGPIPE. If we did not install a signal handler for SIGPIPE, the talking end exits.

How Can I Use the Named Pipe Option with Xmgr?

How Does Xmgr Handle a Named Pipe?

If xmgr is used with the named pipe option, it starts a timer that ticks in intervals of 1000 milliseconds. You can change that interval using the "-timer" command line option. When the first tick occurs, xmgr opens and reads the named pipe and executes the corresponding commands. At the second tick, xmgr closes the pipe. At the third tick, xmgr reopens the pipe, and so forth. In other words, every two seconds we give xmgr a one-second break to respond to commands from the graphic user interface if the commands at the named pipe arrive faster than xmgr can execute them.

How Do I Implement My "Talking End"?

There are at least two different ways how to implement the talking end (your number-cruncher).

First way

  1. Create a fifo using the "mkfifo" command from the shell prompt.
  2. Start your number-cruncher and let it open the named fifo's filename for writing. You should send xmgr commands to the pipe.
  3. Start xmgr with the named pipe option from the shell prompt ("xmgr -npipe /tmp/mypipe")

Second way

  1. Start your program. Your program creates the named pipe using the "mkfifo" library call.
  2. Your program forks (see "man fork").
  3. The child process executes xmgr with the named pipe option.
  4. The parent process opens the named pipe for writing, generates data, and sends commands to the pipe.

As described above, xmgr will close the named pipe every two seconds. Therefore, your talking end will receive the signal SIGPIPE every two seconds as long as the named pipe is open for writing. The default action of a program is to exit on SIGPIPE. Hence, you need to tell your program to ignore SIGPIPE's:

    #include <signal.h>
    /* Don't exit on SIGPIPE */
    signal (SIGPIPE, SIG_IGN);

If you try to write to the pipe while xmgr has closed its listening end, the thus written commands are lost - xmgr will never see them. Therefore, after each write to the pipe you should test whether the write was successful. If it was not, the write operation needs to be repeated later.

One way to handle this problem is to append new commands to a global buffer instead of sending them directly. Each time we append some new commands to that buffer, we subsequently try to write the buffer to the pipe. If the write operation succeeds, we clear the buffer. If it fails, we try to resend the contents of the buffer next time we append a new command.

There is still a problem we need to take care of: it might happen that we always try to write to the pipe when xmgr is not listening. For example, if we send new data to xmgr approximately every two seconds, there is a good chance that we find xmgr not listening for quite a long time. This can lead to an overflow of the buffer. Therefore, each time we append some new commands to the buffer we should check whether the buffer filling exceeds some threshold. If it does, we should wait for xmgr's next sensitive time window and send the entire buffer before continuing our calculations.

Isn't there a simpler solution?

Yes, there is. You can use the acegr_np library that comes with xmgr and implements the solutions suggested in the above.