[comp.windows.x] XtAddInput problem

staatsvr@asdcds.cds.wpafb.af.mil ( Vern Staats;;) (11/10/89)

In article <8911081827.AA12003@turnpike.sun.com> argv@SUN.COM (Dan Heller) writes:
>If you've been reading comp.windows.x, you'll see a very short-lived
>discussion about how XtAddInput is broken --I recommend *strongly*
>that you avoid using it till it's fixed.  This includes R4; I checked
>the source.  The problem is that your routine gets called continuously
>rather than, as the spec says, "when there is data to be read."
>

  I have had some success using XtAppAddInput on our Ultrix 3.1 Vaxen.  
In particular, I am sending commands to the X11 application over a 
named pipe (fifo), which is opened with the O_NDELAY flag to return 
errors rather than blocking on reads.  I was able to prevent the input 
callback routine from being triggered continuously by opening the pipe 
for both reading and writing in the X11 program, even though the program 
never writes to the pipe.

  The man page for read(2) says that with O_NDELAY set, read will return
0 to indicate end-of-file rather than the -1 error return if no process 
has the pipe open for writing.  My guess is that the select call in
WaitForSomething() is also affected by whether a process has the pipe 
open for writing or not.
 
  Even after opening a dummy_write file descriptor I was getting two 
calls to the input callback routine for every actual input I sent it.
Putting calls to XtRemoveInput() and XtAddInput() in the input callback
routine (to destroy and then re-install itself) prevents the second call,
although I suspect it may be more efficient just to live with it and do
a quick return in the callback if ioctl says the pipe is empty.

----                                                                       ///
INET:  staatsvr@asd.wpafb.af.mil      Vern Staats  (513) 255-2714      \\\///
UUCP:  -Maybe- nap1!asd!staatsvr       ASD/SCED   WPAFB OH 45433        \XX/
Boolean orthogonal(char *my_opinions, char *employer_opinions)  {return(TRUE);}

bjaspan@athena.mit.edu (Barr3y Jaspan) (11/10/89)

I've seen random discussion about the use of XtAddInput() and about
how it supposedly does not work.  It *does* work, and I will attempt
to clear up the confusion.

Anyone who is familiar with UNIX realizes that the intrinsics use
select() to detect X and other events, and that XtAddInput() merely
adds filedescriptors to the parameters passed to select.  The man page
for select() says that it returns when there a file descriptor is
"ready for reading, ready for writing, ..."

The confusion, I think, is in "ready for writing."  That means that
the fd is in a state such that data CAN be written to it, not that
data actually has been written to it.  Specifically, stdout is ALWAYS
"ready for writing" (unless it has been messed with) and so *IF YOU DO
AN XtAddInput ON FD 1, IT WILL TO CALL THE FUNCTION CONSTANTLY* and
that is the correct behavior.

For input events, XtAddInput also does the right thing: it calls the
function whenever there is data waiting to be read.  For example, if
you do XtAddInput() on fd 0 with XtInputReadMask, the callback will be
called when the user has typed a line (or a character in cbreak mode).
The function IS NOT called constantly.

As proof of my argument, I present the following source code and
execution output.

--- 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);
}

--- snip snip ---

~% cc -I/mit/x11/include -L/mit/x11/vaxlib input-example.c -lXaw -lXmu -lXt -lX11
~% a.out
testing
Input received on fd 0.
"testing"
Hi there!!
Input received on fd 0.
"Hi"
^C~%

Barr3y Jaspan, MIT-Project Athena
bjaspan@athena.mit.edu
Barry Jaspan, MIT-Project Athena
bjaspan@athena.mit.edu

argv%turnpike@Sun.COM (Dan Heller) (11/10/89)

In article <15783@bloom-beacon.MIT.EDU> bjaspan@athena.mit.edu (Barr3y Jaspan) writes:
> I've seen random discussion about the use of XtAddInput() and about
> how it supposedly does not work.  It *does* work, and I will attempt
> to clear up the confusion.

Ironic -- I was just about to post a followup to my previous message
but you beat me to it.  Well, you said just about what I was going to
say.  I was mistaken about a couple of things which caused the confusion.
Agreed -- this function _does_ work.  However, there is a case where
it's difficult to tell :-)  I think someone pointed out the problem in
another message recently, but didn't emphasize it strongly enough and I
think it should be because it also addresses the confusion I mentioned
above.

I originally said that XtAddInput didn't work because it called your
routine constantly whether there was input there to be read or not.
This isn't true _until it reaches EOF_.  Because the file descriptor
has been modified (FNDELAY I believe), when you're reading on a *file*
and you get to the end of the file, read() no longer returns -1, it
returns 0 and your function is called.  It's hard to tell what the
right thing to do is -- if you want to monitor a file (ala tail -f),
then you're going to have to put up with your function getting called
all the time and your having to do ioctl(fd, FIONREAD, &n) to find
out if there is anything actually there to be read.  The problem here
is that your cpu-usage goes thru the roof.

This is not a problem on pipes and other types of file descriptors.
dan

gjc@mga.COM (George J. Carrette) (11/11/89)

XtAddInput works great in VMS, exactly as one would expect, with no
funny glitches. The only problem is that it limits you directly to a
QIO level interface, (or an AST EVENT/FLAG one if you are clever).
Getting access to the usual record-management-services level is a bit
tricky. But being able to use the VAX-C run-time-library with its
fopen/open and getc/puts read/write is hopeless (undocumented
internals structure). 

So the portability aspects of the thing are hopeless, unless you punt
the DIGITAL provided C-runtime-library for your own. 

-gjc

mvh@cfa250.harvard.edu (Mike VanHilst) (11/12/89)

In article <15783@bloom-beacon.MIT.EDU> bjaspan@athena.mit.edu
(Barr3y Jaspan) writes:
> I've seen random discussion about the use of XtAddInput() and about
> how it supposedly does not work.  It *does* work, and I will attempt
> to clear up the confusion.

and procedes to explain how select() works on stdin and stdout.

While in article <294@nap1.cds.wpafb.af.mil>
 staatsvr@asdcds.cds.wpafb.af.mil ( Vern Staats;;) writes:

>  I have had some success using XtAppAddInput on our Ultrix 3.1 Vaxen.
>In particular, I am sending commands to the X11 application over a
>named pipe (fifo), which is opened with the O_NDELAY flag to return
>errors rather than blocking on reads.  I was able to prevent the input
>callback routine from being triggered continuously by opening the pipe
>for both reading and writing in the X11 program, even though the program
>never writes to the pipe.

I too, am using name pipes as a convenient mechanism for remote
control of my application while having a functioning user
interface.  My experience has been that problems such as those
discussed above, have much to do with the behavior of the pipe
under different conditions, on different operating systems, and
little to do with how select() is called.  It seems that the
behavior is not precisely defined for BSD4.3 and may differ among
vendors.

To wit: under SunOS 3.5 and 4.0.3, if a pipe is opened to read
and set to block (I open it with O_NDELAY, so as not to block
on the open(), and then use fcntl() without the O_NDELAY, to set
blocking), select() only returns with its flag when something
is in fact available to be read (I never find a read of 0 in
response to being called).  Under Ultrix 3.1, the pipe cannot
block if the pipe is not currently opened for writing by some
process.  It always returns from select(), and fcntl() cannot
change this condition.  (This condition, BTW, is not consistent
as, on occasion, it functions as on the Sun).  Under Ultrix 2.0,
it could be set to block by fcntl(), but reverted to non-blocking
when the writer opened and then closed its end.

The method that seems to work in all cases is to open the pipe
on another descriptor for writing (as a dummy), and not close
that connection until closing the connection for reading.  There
is a catch, in that opening to write may fail if there is no
reader, so the sequence, open to read non-blocking, open the
dummy to write, fcntl() the reader to block, must be followed.
I ifdef this out for the Sun, due to a desire to preserve its
fewer available descriptors, as mentioned in another discussion.
I have not tried opening the pipe read/write as Vern suggests.
My suggestion for any new port, is always to test with tiny
programs just to open for writing and reading with select() to
test that OS's behavior.

Now, if somebody could explain this procedure under SysV (i.e.
Apollo), I would be much obliged.

------
Mike VanHilst 				mvh@cfa.harvard.edu
Smithsonian Astrophysical Observatory	(617)495-7260
60 Garden Street, Cambridge, MA  02138