[comp.windows.x] How do I get XtAppAddInput to work correctly

wdavis@x102c.harris-atd.com (davis william 26373) (01/05/91)

In article <1990Dec31.170612.7040@lta.com> 
with Subject: Frequently Asked Questions about X with Answers 3/3
   [long monthly posting]
xug@lta.com (X User's Group) writes:
>Subject: 72)  Why does XtAppAddInput not work as described?
>I am using XtAppAddInput to read from a file, but the function is called even
>when there isn't input pending.
>
>	XtAppAddInput is actually working as it is supposed to. When used on
>files, it is called whenever the file is READY to be read, not when there is
>new data to be read. The file is almost always ready to be read, however, if 
>only because you can spin back to the beginning and read data you've read 
>before.

This sounds like a rationalization of why the bug is now to be
called a feature.  Given this explanation, any open file descriptor
can be considered "ready to be read".

> The result is that your function will almost always be called every
>time around XtMainLoop().

Right - and that is a bug - not a feature!

>	To get the type of interaction you are expecting, add this line to
>the beginning of your function to test whether there is new data:
>	     if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) return;
>
>[courtesy Dan Heller (argv@ora.com); 8/90]

But this does not address the original problem because it is still
calls the procedure even when there is nothing really ready to be
read.  I already have a check for when the read of the file descriptor
returns a zero to ignore the call.  Lots of overhead.  But the bigger
problem is that when there is a file descriptor that is always ready to
read (because it is always open) then the Work Process never gets
called.  So, how do I get the brain damaged Xt logic to stop calling
my input function long enough to execute a background work process?
I do not want to close the file descriptor because I will not know
what I have already read when I open it again.  I do not want to
remove the input because then I will have no way to know that the
file has been appended to (or is there a way to know that?).
And I cannot avoid using a background work process because that
code is part of a purchased package.

I will remove the input for X if there is a way to know when the
file has something more added to the end of what has already been
read.  Any ideas on this?  Lacking that is there any chance that
the Xt logic will get fixed?  How about an option (we could call it
XT_GLITCH_INHIBIT) that will make the input stuff work rationally?
Or is this really an underlying problem with the select call?

My preference is for a work around that will get me past my short
term problem without an Xt rewrite.  I can tolerate the extra overhead
if the Work Process will just get executed.

bjaspan@athena.mit.edu (Barr3y Jaspan) (01/05/91)

In article <5184@trantor.harris-atd.com>, wdavis@x102c.harris-atd.com (davis william 26373) writes:
|> >I am using XtAppAddInput to read from a file, but the function is called even
|> >when there isn't input pending.
|> >
|> >	XtAppAddInput is actually working as it is supposed to. When used on
|> >files, it is called whenever the file is READY to be read, not when there is
|> >new data to be read.

wdavis is incorrect; XtAddInput calls the procedure when there is DATA TO
READ.  All XtAddInput does, for functions registered with XtInputReadMask, is
call select and then calls those procedures whose filedescriptor is set in the
fdset returned from select.  Perhaps he was thinking of the case of
XtInputWriteMask, for which the associated procedure *IS* called constantly
(since files/pipes/etc are almost always ready to be written to).  (well, of
course, XtAddInput doesn't so that, it causes XtMainLoop to.)

I have only used XtAddInput for filedescriptors connected to
pipes/sockets/etc, never for a file.  I assure you that it does, in fact,
work.  At the end of the posting there is a sample program demonstrating its
use.

Perhaps your problem is the result of peculiar select() semantics.  When EOF
is reached on a filedescriptor, select() will return that *there is data
waiting to be read on it* even though there isn't.  Any  subsequent read()
from that descriptor will return 0 bytes, which indicates that it has been
closed (and the program should deal accordingly; in this case, remove the
input procedure).  Howver, future calls to select() on the descriptor will
CONTINUE to indicate that there is data waiting to be read.

So, if your program is not checking that EOF has been reached, the input
procedure will get called constantly once it has been.

Sorry if this is poorly worded, I'm on a slow net connection and can't fix it.

Barr3y Jaspan, bjaspan@mit.edu
Watchmaker Computing

--- snip snip ---

#include <stdio.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Label.h>

void input();

main(argc, argv)
   int argc;
   char **argv;
{
     Widget	top;

     top = XtInitialize("Hello", "World", NULL, 0, &argc, argv);
     (void) XtCreateManagedWidget("label", labelWidgetClass, top,
				  NULL, 0);
     
     XtAddInput(0, XtInputReadMask, input, NULL);
     XtRealizeWidget(top);
     XtMainLoop();
}

void input(client_data, source, input_id)
   caddr_t	client_data;
   int		*source;
   XtInputId	*input_id;
{
     char	buf[BUFSIZ];
     
     printf("Input received on fd %d.\n", *source);
     scanf("%s", buf);
     printf("\"%s\"\n", buf);
}

mouse@lightning.mcrcim.mcgill.EDU (01/05/91)

>>> I am using XtAppAddInput to read from a file, but the function is
>>> called even when there isn't input pending.

>> XtAppAddInput is actually working as it is supposed to.  When used
>> on files, it is called whenever the file is READY to be read, not
>> when there is new data to be read.

> wdavis is incorrect; XtAddInput calls the procedure when there is
> DATA TO READ.  All XtAddInput does, for functions registered with
> XtInputReadMask, is call select and then calls those procedures whose
> filedescriptor is set in the fdset returned from select.

This is not "when there is DATA TO READ".  Even ignoring the question
of select()'s semantics when applied to files, it sometimes returns
when no data are available, such as a socket connection which has
reached EOF (ie, all write ends shut down).

> I have only used XtAddInput for filedescriptors connected to
> pipes/sockets/etc, never for a file.

Then why are you making pronouncements about what it - or rather, the
presumably related XtAppAddInput - does when applied to files?

> Perhaps your problem is the result of peculiar select() semantics.
> When EOF is reached on a filedescriptor, select() will return that
> *there is data waiting to be read on it* even though there isn't.
> Any subsequent read() from that descriptor will return 0 bytes, which
> indicates that it has been closed

If it had been closed, the read would return -1 with errno set to EBADF
(unless of course something else had been opened up since, and gotten
the same descriptor).  0 means the descriptor is still open, but at
EOF.

The semantics select() is supposed to provide are not entirely clear.
All the descriptions available to me simply say "ready for reading".  I
have seen this stated as "a read() will not block", which is what I
observe from sockets.  I, too, have not tried using select() on files,
but if it truly does provide "read() will not block" indications, then
select()ing a file for reading will always indicate readability.
(Unless, presumably, the file is on a dead NFS server :-)

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

wdavis@x102c.harris-atd.com (davis william 26373) (01/08/91)

I quoted from Part 3 of Frequently Asked Questions:
>>>> I am using XtAppAddInput to read from a file, but the function is
>>>> called even when there isn't input pending.
>>> XtAppAddInput is actually working as it is supposed to.  When used
>>> on files, it is called whenever the file is READY to be read, not
>>> when there is new data to be read.

In article <1991Jan4.221250.5174@athena.mit.edu>, bjaspan@athena.mit.edu (Barr3y Jaspan) writes:
>> wdavis is incorrect; XtAddInput calls the procedure when there is
>> DATA TO READ.

Which is exactly what I expected from the documentation.
When this didn't work, I posted my query.

>>  All XtAddInput does, for functions registered with
>> XtInputReadMask, is call select and then calls those procedures whose
>> filedescriptor is set in the fdset returned from select.

Actually, I was asking about XtAppAddInput as my documentation on
Xt Intrinsics claims that XtAddInput is obsolete and has been replaced
by XtAppAddInput.  The only difference in the documentation
is that the obsolete one always uses the default application context
and the one I was using requires you to specify it.  I got the
context from the ApplicationShell widget surrounding a list that
was being kept updated from the bottom of a file.  I assume the
internals are similar for both routines.

In article <9101051037.AA02347@lightning.McRCIM.McGill.EDU> mouse@lightning.mcrcim.mcgill.EDU writes:
>This is not "when there is DATA TO READ".  Even ignoring the question
>of select()'s semantics when applied to files, it sometimes returns
>when no data are available, such as a socket connection which has
>reached EOF (ie, all write ends shut down).

Having Xt Intrinsics claim to provide a function when it just uses
underlying select semantics is misleading.  If XtAppAddInput wants
to claim it provides a particular function, then it should provide
that function.  If it just intends to be a wrapper for select then
that is what it should claim.  At least then it becomes a simple
matter to test out select semantics and know what to expect for
a particular release of Unix.

>> I have only used XtAddInput for filedescriptors connected to
>> pipes/sockets/etc, never for a file.
>
>Then why are you making pronouncements about what it - or rather, the
>presumably related XtAppAddInput - does when applied to files?
>
>> Perhaps your problem is the result of peculiar select() semantics.
>> When EOF is reached on a filedescriptor, select() will return that
>> *there is data waiting to be read on it* even though there isn't.
>> Any subsequent read() from that descriptor will return 0 bytes, which
>> indicates that it has been closed
>
>If it had been closed, the read would return -1 with errno set to EBADF
>(unless of course something else had been opened up since, and gotten
>the same descriptor).  0 means the descriptor is still open, but at
>EOF.
>
>The semantics select() is supposed to provide are not entirely clear.
>All the descriptions available to me simply say "ready for reading".  I
>have seen this stated as "a read() will not block", which is what I
>observe from sockets.  I, too, have not tried using select() on files,
>but if it truly does provide "read() will not block" indications, then
>select()ing a file for reading will always indicate readability.
>(Unless, presumably, the file is on a dead NFS server :-)
>
>					der Mouse

Well, based on this discussion, I wrote a simple program to test
select semantics on files and pipes.

For the version of Unix I am using (your mileage may vary), select
will block when used on an open pipe with no data (as expected).
It also blocks after a read of an end of file indication (end of
files can be put into a stream and followed by more data without
bothering select).  Multiple end of file indications without
intervening data cause the pipe to be closed.  I did my pipe testing
with a simple "cat" command and did not investigate how the Ctrl-D
typed to cat actually translated to an EOF down the pipe.

When select was applied to a file, the select returned "ready for
reading" (from select(2) documentation here) when there was data
to read or the current file position was at end of file.  I did
not try positioning the file position to weird places as this would
not have helped my application (I need to track things as they
are written to a file).

So, the conclusion is that XtAppAddInput on files is only useful
to read in a file one time.  And it is clearly the hard way to go
about it.  I only see that as useful if the file read needs to
proceed in parallel with user events (a big file or something that
needs to allow a user cancel event during the read).

Lacking any better suggestions (one suggestion here involved generating
special X events to indicate it was time to look at the file), I decided
to fork a process on a pipe.  I forked a copy of "tail +0f filename"
and use XtAppAddInput on the read side of the pipe.  It seems to
be working fine.

Long term, it would be desirable to have X provide a lower overhead
method of doing this, but I don't know a good way at the moment
when select does not want to provide the functionality.  What it
sounds like is needed for this type of application (basically a
fancy tail command under X) is to have a way to specify to Unix
that select should not consider a file at end of file as readable.
In this way, a program could read to the end of file, change (via
fcntl) the select semantics and not be bothered until something
became appended.  Then , if desired, the select semantics could be
toggled if it was important not to get tied up with a read of all
available input in one call to the input function.

moss@brl.mil (Gary S. Moss (VLD/VMB) <moss>) (01/10/91)

In article <5184@trantor.harris-atd.com>, wdavis@x102c.harris-atd.com (davis william 26373) writes:
|> 
|> I will remove the input for X if there is a way to know when the
|> file has something more added to the end of what has already been
|> read.  Any ideas on this?

The proper semantics of available data for input are clear when
reading from a pipe or socket, but when reading from a file, it becomes
something of a religious argument.  I was fooled by the documentation
also, so perhaps it should be tweaked.  Anyway, I have combined the
XtAppAddInput with code which checks for available input in the file.
It is fairly simple so I have included it below; the application
monitors a log file (like running a "tail -f" into an AsciiText widget.
This doesn't solve the problem of your registered function being called
in a tight loop so that your work proc doesn't get scheduled, but you
could perhaps not use XtAppAddInput at all.

Hope this helps,
-Gary

...

	/* Register error log monitoring function. */
	(void) XtAppAddInput( apcon, fileno(errorfp), XtInputReadMask,
				displayLogMsg, (XtPointer) errorfp );
	/* Loop to handle events. */
	XtAppMainLoop( apcon );

...

/*
	void displayLogMsg( XtPointer client_data, int *source, XtInputId *id )

	Display the next line of the log file in the "errors" text widget.
 */
/*ARGSUSED*/
STATIC void
displayLogMsg( client_data, source, id )
XtPointer client_data;
int *source;
XtInputId *id;
	{	register int ct;
		long addr; /* save file offset of beginning of line */
		FILE *fp = (FILE *) client_data;
		char buffer[MAXLOGLNLEN];
	if( fp == NULL )
		return; /* monitoring of log errors turned off */
	addr = ftell( fp );
	if( fgets( buffer, MAXLOGLNLEN, fp ) != NULL )
		{	register int len = strlen( buffer );
		/* if( len <= 1 )
			return; /* ignore blank lines */
		if( buffer[len-1] != '\n' )
			{ /* A missing newline means that a write to the
				log is in progress, so we are done. */
			(void) fseek( fp, addr, 0 ); /* beginning of line */
			return;
			}
		sendToTextWidget( XtNameToWidget( toplevel, "main.errors" ),
				buffer );
		}
	else
		clearerr( fp ); /* for 4.2 BSD or SUNs, need to clear EOF */
	}