Notify_error
notify_post_destroy(client, status, when)
Notify_client client;
Destroy_status status;
Notify_event_type when;
NOTIFY_INVAL
is returned if status or when is not defined. After notifying a client to
destroy itself, all references to client are purged from the Notifier.
20.7.5 Delivery Time of Destroy Events
Unlike a client event notification, the Notifier does not try to detect when it is safe to post a
destroy notification. Thus, a destroy notification can come at any time. It is up to the good
judgement of a caller of notify_post_destroy() or notify_die() (described in
Section 20.10, “Notifier Control”) to make the call when a client is not likely to be in the
middle of accessing its data structures.
If status is DESTROY_CHECKING and the argument when is NOTIFY_IMMEDIATE, then
notify_post_destroy() may return NOTIFY_DESTROY_VETOED, if the client does not
want to go away. See Section 20.9.5, “Modifying an Objects’ Destruction,” for details on
these values.
Often you want to tell a client to go away at a safe time. This implies that delivery of the
destroy event will be delayed, in which case the return value of notify_post_des-
troy() cannot be NOTIFY_DESTROY_VETOED because the client has not been asked yet. To
get around this problem, the Notifier will flush the destroy event of a checking/destroy pair of
events if the checking phase is vetoed. Thus, a common idiom is:
(void) notify_post_destroy(client, DESTROY_CHECKING, NOTIFY_SAFE);
(void) notify_post_destroy(client, DESTROY_CLEANUP, NOTIFY_SAFE);
20.8 Reading and Writing Through File Descriptors
The Notifier is set up for testing whether or not there is input pending on file descriptors and
whether file descriptors are available to accept data for writing. The file descriptors can rep-
resent files, pipes, and sockets. System calls such as read() or write() can be used on
any of these file descriptors.
In an event-driven system, the application cannot wait for data to be read. Instead it is better
for the system to notify the application when data can be read. System calls such as read()
will block if there is no input to be read. That is, read() waits until there is something to
read before it returns. If your application is blocking on a read, then it cannot process events
that the user may be generating, such as selecting a panel button. Rather than blocking on a
call to read(), the Notifier can inform you when there is input ready on that file descriptor
so that when you finally call read(), it returns immediately.
Notifier
The Notifier 479
To handle this, the Notifier provides functions such as notify_set_input_func() and
notify_set_output_func() to test whether a file descriptor has data to be read or is
ready for writing. These functions inform you of the status of file descriptors, whether they
are files, pipes, or sockets.*
Notify_func
notify_set_input_func(client, input_func, fd)
Notify_client client;
Notify_func input_func;
int fd;
Notify_func
notify_set_output_func(client, output_func, fd)
Notify_client client;
Notify_func output_func;
int fd;
input_func() is called whenever the file descriptor (fd) has data to be read.† out-
put_func() is called whenever the file descriptor (fd) is ready to receive data.
Generally, file descriptors open for writing are always ready to receive data, so this function
may not be as widely used as the input function case. However, it is important if you are
writing to a pipe where another process is probably on the other side of the pipe. Pipes have
buffers which, when full, will not accept any more data on them until the process on the other
side of the pipe reads the data written so far (thus emptying the buffer). If the process on the
other side of the pipe is slow in reading the data you have written to it, then you may need to
use notify_set_output_func() so that you can be notified when the pipe is ready to
have more data written to it.
input_func() and output_func() take the following form:
Notify_value
func(client, fd)
Notify_client client;
int fd;
20.8.1 Reading Files
Our first example demonstrates how notify_set_input_func() can be used to read
data from a file. The program is simple; it does not bother using any XView objects such as
frames or canvases. Because of this, it makes use of notify_start(), which is covered
in Section 20.10, “Notifier Control.”
*notify_set_exception_func() is also available for determining if out-of-band data is available on a sock-
et. Its format is described in the XView Reference Manual.
†Exceptions to this are discussed in Section 20.8.1, “Reading Files.”
480 XView Programming Manual
Example 20-3. The notify_input.c program
/*
* notify_input.c -- use notify_set_input_func to monitor the state of
* a file. The Notifier is running and checking the file descriptors
* of the opened files associated with the command line args. The
* routine installed by notify_set_input_func() is called whenever
* there is data to be read. When there is no more data to be read
* for that file, the input function is unregistered. When all files
* have been read, notify_start() returns and the program exits.
*/
#include <stdio.h>
#include <sys/ioctl.h>
#include <xview/notify.h>
main(argc, argv)
char *argv[ ];
{
Notify_value read_it();
Notify_client client = (Notify_client)10101; /* arbitrary */
FILE *fp;
while (*++argv)
if (!(fp = fopen(*argv, "r")))
perror(*argv);
else {
(void) notify_set_input_func(client, read_it, fileno(fp));
client++; /* next client is new/unique */
}
/* loops continuously */
notify_start();
}
/*
* read_it() is called whenever there is input to be read. Actually,
* it is called continuously, so check to see if there is input to be
* read first.
*/
Notify_value
read_it(client, fd)
Notify_client client;
int fd;
{
char buf[BUFSIZ];
int bytes, i;
if (ioctl(fd, FIONREAD, &bytes) == -1 || bytes == 0)
(void) notify_set_input_func(client, NOTIFY_FUNC_NULL, fd);
else
do
if ((i = read(fd, buf, sizeof buf)) > 0)
(void) write(1, buf, i);
while (i > 0 && (bytes -= i) > 0);
return NOTIFY_DONE;
}
Notifier
The Notifier 481
The comments in this sample program describe what is going on up front. However, behind
the scenes, there are interesting things happening.
The first thing to notice is that the clients used in the Notifier start at 10101 and, for each file
on the command line, the client is incremented by one. Remember, the client is an arbitrary
identifier and can be any unique value. Since there are no other clients of the Notifier, we
know that 10101 is going to be unique.
The function read_it() is installed to read data from the files given on the command line.
The first thing that read_it() does is check if there is data to be read. The ioctl() call
returns the number of bytes to read in the bytes variable. If there is data to be read, then
read() is used to read buffers (of size BUFSIZ) until it has read all the bytes pending. This
continues until the program has read the entire file. Once this happens, the ioctl() call
will return that there are no bytes to read. Normally, it would return -1 indicating that the
end of file has been reached. However, the Notifier has modified the state of the file descrip-
tor for internal purposes (to set nonblocking mode). When we have reached the end of the
file, we unregister the client by calling notify_set_input_func() with a
notify_func of NOTIFY_FUNC_NULL.
notify_input.c is not terribly interesting because you do not need to be notified of data to be
read on a file; you can just open the file, read it till EOF, and then close it. But if you know
that the file is going to have data continuously added to it, you might want to be informed
when there is new data to be read and then print it out.
This can be accomplished by not unregistering the input function with the Notifier when
there is no data to be read. Unfortunately, your function is going to be called continuously
whether there is new data to be read or not. This is true only for file descriptors that repre-
sent files and is not the case for pipes.
20.8.2 Reading and Writing on Pipes
A more interesting and likely problem occurs when an application has to execute another
program, send data to it, and read output from it all at the same time. The best way to handle
such a situation is as follows:
Set up a pipe for each stream (such as
stdin and stdout). This is done using the
pipe() system call.
fork() a new process. The child will execute the program (using execvp()) and
have its input and output redirected to the appropriate ends of the pipes (using dup2()).
The parent registers input and output functions to read and write from the “other” ends of
the pipes (using notify_set_input_func()). Also, the parent calls
notify_set_wait3_func(), as discussed earlier.
The example program that demonstrates this capability is a little longer, but it is still simple.
It follows the steps outlined above and is the minimum needed to spawn a new process.
Again, to keep the code short and simple, no XView objects are created. The existence of
XView objects would not affect the program in any way.
482 XView Programming Manual
The way to run this program is to specify a command on the command line of the program.
For example:
% ntfy_pipe cat
Remember, there are two processes involved here: the parent process (ntfy_pipe) and the
child process (cat). Both programs read from their respective stdin and write to their own
stdout. However, because the child process (cat) was spawned off from the parent, we
need to handle the IO (input/output) of the new process. After the call to fork(), its stdin
and stdout must be redirected to the pipe that was set up before the fork. The parent,
however, retains its IOits
stdin and stdout remain directed towards the window in
which you typed the command.
Example 20-4. The ntfy_pipe.c program
/*
* ntfy_pipe.c -- fork and set up a pipe to read the IO from the
* forked process. The program to run is specified on the command
* line. The functions notify_set_input_func() and
* notify_set_output_func() are used to install functions which read
* and write to the process stdin and stdout.
* The program does not use any XView code -- just the Notifier.
*/
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <xview/notify.h>
Notify_client client1 = (Notify_client)10;
Notify_client client2 = (Notify_client)11;
int pipe_io[2][2]; /* see diagram */
/*
* [0 ] [1 ]
* child reads: |========= pipe_io[0] ========| <- parent writes
* pipe_io[0 ][0 ] pipe_io[0][1]
*
* parent reads: |========= pipe_io[1 ] ========| <- child writes
* pipe_io[1 ][0 ] pipe_io[1][1]
*
* The parent process reads the output of the child process by reading
* pipe_io[1][0] because the child is writing to pipe_io[1 ][1 ].
* The child process gets its input from pipe_io[0 ][0 ] because the
* parent writes to pipe_io[0 ][1 ]. Thus, one process is reading from
* one end of the pipe while the other is writing at the other end.
*/
main(argc, argv)
char *argv[ ];
{
Notify_value read_it(), write_it(), sigchldcatcher();
int i, pid;
FILE *fp;
Notifier
The Notifier 483

Get Volume 7A: XView Programming Manual now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.