[comp.unix.wizards] But I don't wanna do non-blocking I/O...

jc@minya.UUCP (John Chambers) (06/11/90)

Subject: But I don't wanna do non-blocking I/O...
Newsgroup: comp.unix.wizards

Well, here's a little puzzle that has me stumped; maybe 
someone out ther can tell me what an idiot I've been for
overlooking ______________  (fill in the blank).

What I've got is an application that I'm trying to port
from BSD to SYS/V (ESIX, actually, 5.3 on a 386).  The
application does stuff (which you don't want to know
about ;-) via a serial port, and it normally runs with
the port in raw mode (-icanon, of course).  Sometimes,
it needs to put the port back into a cooded state and
start up a shell to talk to whatever is on the other
side of the port.  The shell starts up, but the port
is still in some sort of half-baked mode that does
non-blocking I/O.  When the shell reads, it gets eof
immediately and exits.  Durned if I can puzzle out why.

I've added code to dump the status of the struct termio
just before the ioctl is done, and it says:
	File 0:    cflag=004275
	File 0:    iflag=006446
	File 0:    lflag=000073
	File 0:    oflag=014005
I've also had it invoke a shell of my own for which I
have the source so I can make it do an ioctl and show
the result, and it gives exactly the same numbers as
above.  According to the termio(7) manual 'page', the
above lflag bits include the 02 bit, which is ICANON,
so the driver should be assembling input into lines,
and not returning anything until it gets an eol char.
But fgets(buf,n,stdin) returns EOF immediately.

It appears there is some way of doing non-blocking I/O
on Sys/V which is unknown to me (and which I can't find
in TFM), but which is known to this program.  I've tried
grepping for all the likely suspects.  There are no calls
of fcntl.  The only calls of ioctl are the ones that set
raw mode initially and the above cooked mode just before
calling a shell.  The open() does include F_NDELAY, but
this is supposed to apply only to the open; it shouldn't
set non-blocking I/O permanently for the file (I hope ;-).
The string "NBIO" does not occur in the code, in upper 
or lower case.

An especially curious aspect is that this code starts
shells successfully on BSD and Ultrix systems, and also
on a SYS/V.2 system that I tried it on.  The non-blocking
I/O doesn't happen there, only on 5.3.

Does anyone know how else one might enable non-blocking
I/O on a Sys/V.3 serial port?  Where might I look in the
manual that I haven't mentioned above?  And how should I
suppress non-blocking I/O?

[Non-blocking I/O certainly isn't one of the more-portable
or better-documented parts of Unix. ;-]

-- 
Uucp: ...!{harvard.edu,ima.com,mit-eddie.edu}!minya!jc (John Chambers)
Home: 1-617-484-6393
Work: 1-508-952-3274
Cute-Saying: It's never to late to have a happy childhood.

jc@minya.UUCP (John Chambers) (06/12/90)

Well, here's a little puzzle that has me stumped; maybe 
someone out there can tell me what an idiot I've been for
overlooking ______________  (fill in the blank).

What I've got is an application that I'm trying to port
from BSD to SYS/V (ESIX, actually, 5.3 on a 386).  The
application does stuff (which you don't want to know
about ;-) via a serial port, and it normally runs with
the port in raw mode (-icanon, of course).  Sometimes,
it needs to put the port back into a cooked state and
start up a shell to talk to whatever is on the other
side of the port.  The shell starts up, but the port
is still in some sort of half-baked mode that does
non-blocking I/O.  When the shell reads, it gets eof
immediately and exits.  Durned if I can puzzle out why.

I've added code to dump the status of the struct termio
just before the exec is done, and it says:
	File 0:    cflag=004275
	File 0:    iflag=006446
	File 0:    lflag=000073
	File 0:    oflag=014005
I've also had it invoke a shell of my own for which I
have the source so I can make it do an ioctl and show
the result, and it gives exactly the same numbers as
above.  According to the termio(7) manual 'page', the
above lflag bits include the 02 bit, which is ICANON,
so the driver should be assembling input into lines,
and not returning anything until it gets an eol char.
But fgets(buf,n,stdin) returns EOF immediately.  My
(mis?)reading of the termio man page says that the 
above flags couldn't possibly do non-blocking I/O,
but that's what happens.

I've also had my shell do a 10-sec wait before exiting,
so I can go to another window and run stty on the port;
the stty agrees with all the bits in the above flags,
in particular it says that icanon is on.

It appears there is some way of doing non-blocking I/O
on Sys/V which is unknown to me (and which I can't find
in TFM), but which is known to this program.  I've tried
grepping for all the likely suspects.  There are no calls
of fcntl.  The only calls of ioctl are the ones that set
raw mode initially and the above cooked mode just before
calling a shell.  The open() does include F_NDELAY, but
this is supposed to apply only to the open; it shouldn't
set non-blocking I/O permanently for the file (I hope ;-).
The string "NBIO" does not occur in the code, in upper 
or lower case.

An especially curious aspect is that this code starts
shells successfully on BSD and Ultrix systems, and also
on a SYS/V.2 system that I tried it on.  The non-blocking
I/O doesn't happen there, only on 5.3.

Does anyone know how else one might enable non-blocking
I/O on a Sys/V.3 serial port?  Where might I look in the
manual that I haven't mentioned above?  And how should I
suppress non-blocking I/O?

[Non-blocking I/O certainly isn't one of the more-portable
or better-documented parts of Unix. ;-]


-- 
Uucp: ...!{harvard.edu,ima.com,mit-eddie.edu}!minya!jc (John Chambers)
Home: 1-617-484-6393
Work: 1-508-952-3274
Cute-Saying: It's never to late to have a happy childhood.

sar0@cbnewsl.att.com (stephen.a.rago) (06/12/90)

In article <397@minya.UUCP>, jc@minya.UUCP (John Chambers) writes:
> Subject: But I don't wanna do non-blocking I/O...
> Newsgroup: comp.unix.wizards
> 
> Well, here's a little puzzle that has me stumped;
...
> But fgets(buf,n,stdin) returns EOF immediately.
...
> 
> The open() does include F_NDELAY, but
> this is supposed to apply only to the open; it shouldn't
> set non-blocking I/O permanently for the file (I hope ;-).

Here's your problem.  If you open with O_NDELAY, your file
descriptor is in non-blocking mode until you explicitly turn
it off with fcntl.  O_NDELAY affects more than just the open.

Steve Rago
sar@attunix.att.com

cpcahil@virtech.uucp (Conor P. Cahill) (06/13/90)

In article <397@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
>
>  [story of program where fgets() returns EOF on first read deleted]
>
>calling a shell.  The open() does include F_NDELAY, but
                                           ^^^^^^^^  I assume you mean O_NDELAY
>this is supposed to apply only to the open; it shouldn't
>set non-blocking I/O permanently for the file (I hope ;-).

You should RTFM (specifically the read(2) man page). Therein you should
find a paragraph similar to the following:

	When attempting to read a file associated with a tty that has no data
	currently available:

		If O_NDELAY is set, the read will return 0.
		If O_NDELAY is clear, the read will block until data becomes
		available.

If you don't want this behavior, you should use the fcntl(2) to change turn
off the O_NDELAY once the open has returned.

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

jc@minya.UUCP (John Chambers) (06/14/90)

In article <1990Jun12.203245.5804@virtech.uucp>, cpcahil@virtech.uucp (Conor P. Cahill) writes:

Hey, thanks for the help!  Needless to say, I *do* have further comments...
(;-)

> >calling a shell.  The open() does include F_NDELAY, but
>                                            ^^^^^^^^  I assume you mean O_NDELAY

Yah; that was a typo.  Too bad there isn't a version of lint for news
articles.  But being a human, you guessed what I meant.

> >this is supposed to apply only to the open; it shouldn't
> >set non-blocking I/O permanently for the file (I hope ;-).
>
> You should RTFM (specifically the read(2) man page). Therein you should
> find a paragraph similar to the following:
> 	When attempting to read a file associated with a tty that has no data
> 	currently available:
> 		If O_NDELAY is set, the read will return 0.
> 		If O_NDELAY is clear, the read will block until data becomes
> 		available.

Hey, whadday you know, I actually found it in one copy of TFM.  I first
looked it up in various other Unix manuals I happen to have lying about
(souvenirs, you know), and didn't find it in them, but I just got a copy
of the System V/386 Release 32 (really!) Programmer's Reverence Manual
(that was a typo, too, honest it was, but it's too good to correct ;-), 
and there is is in black and white....  In the morning, I'll check with 
the Ultrix manuals at work and see what they say.  If they're true to
form, they won't mention the subject; anyway, the code works find on
Ultrix, since I figured out that PMAX/Ultrix doesn't do non-blocking I/O.

> If you don't want this behavior, you should use the fcntl(2) to turn
> off the O_NDELAY once the open has returned.

Which reminds me that the ATT people seem to be unlearning what little
they used to know about writing manual pages.  The fcntl(2) page doesn't
even come close to explaining the situation.  It does refer the reader
to fcntl(5), but even then the explanation isn't exactly obvious to the
meanest intelligence.  For starters, it gives a code (F_SETFL) for setting
flags, but it doesn't have a code for resetting the flags once they've
been set, which is what I wanted to do.  This was a bit of a puzzle, 
until I finally re-oriented my brain and guessed what they were hinting
at.  (OK, I've been programming too long; what can I say? But I won't
give away the ending to those who haven't read the book... ;-)  

Anyhow, that did solve the problem.  It also meant that I expanded my
#defines to include SYS5_2 and SYS5_3.  In any case, it looks like one 
more thing to remember when trying to write portable code.  It ain't 
getting easier.  I can hardly wait for the Sys/5.4 release.  By then
maybe I'll be porting stuff to Mach, and going totally insane.

(Now if I could only decrypt what the manuals say about process groups. :-)

-- 
Uucp: ...!{harvard.edu,ima.com,eddie.mit.edu}!minya!jc (John Chambers)
Home: 1-617-484-6393
Work: 1-508-952-3274
Cute-Saying: It's never to late to have a happy childhood.

cpcahil@virtech.uucp (Conor P. Cahill) (06/14/90)

In article <407@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
>
>Anyhow, that did solve the problem.  It also meant that I expanded my
>#defines to include SYS5_2 and SYS5_3.  In any case, it looks like one 
>more thing to remember when trying to write portable code.  It ain't 
>getting easier.  I can hardly wait for the Sys/5.4 release.  By then
>maybe I'll be porting stuff to Mach, and going totally insane.

One suggestion:

Do not add a #define for SYS5_anything.  This gets you into all kinds of
problems when you put your stuff on hybrid systems (like 5.4).  Instead
use something like:

#define HAS_NDELAY_READS

which is the explicit capability you are checking about.


-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

jc@minya.UUCP (John Chambers) (06/15/90)

In article <407@minya.UUCP>, jc@minya.UUCP (John Chambers) writes:

[Well, here I go doing a followup to my own article... ;-]

> > You should RTFM (specifically the read(2) man page). Therein you should
> > find a paragraph similar to the following:
> > 	When attempting to read a file associated with a tty that has no data
> > 	currently available:
> > 		If O_NDELAY is set, the read will return 0.
> > 		If O_NDELAY is clear, the read will block until data becomes
> > 		available.
> 
> Hey, whadday you know, I actually found it in one copy of TFM.  I first
> looked it up in various other Unix manuals I happen to have lying about
> (souvenirs, you know), and didn't find it in them, but I just got a copy
> of the System V/386 Release 32 (really!) Programmer's Reverence Manual
> (that was a typo, too, honest it was, but it's too good to correct ;-), 
> and there is is in black and white....  In the morning, I'll check with 
> the Ultrix manuals at work and see what they say.  If they're true to
> form, they won't mention the subject; anyway, the code works find on
> Ultrix, since I figured out that PMAX/Ultrix doesn't do non-blocking I/O.

Well, I looked it up in the Ultrix manuals, and what they have to say is
even more fun than the Sys/V manuals.  The read(2) page doesn't make any
mention of the topic.  The open(2) page says that O_NDELAY and O_NONBLOCK
are the same, and both cause the open to return immediately (which is a
bald-faced lie on the PMAX, but that's a different problem).  There's no
hint as to the behavior of later calls of read().  You are referred to
the fcntl(2) page, which lists F_GETFL and F_SETFL for arg 2, and FNDELAY
as one of the flags for arg 3.  A simple experiment shows that a read()
after an open(...,O_NDELAY|...) does indeed block, but I can't find this
documented anywhere, so perhaps it's best not to rely on it.

The real fun starts when you do a test that seems to verify that a call
of fcntl(f,F_SETFL,FNDELAY) does indeed cause non-blocking I/O, at least
on VAX/Ultrix.  But it turns out that this call is incorrect.  Why?  The
hint is that, while there is are codes for getting and setting the status
flags, there is no code for clearing a flag.  After the further hint that
the correct way to turn a status flag on or off always requires two system
calls, I'll let the reader puzzle the rest out.  Of course, the manual
pages don't contain any actual examples.  (And I could still misunderstand
what they say; it's happened before.)

I've been trying, in essence, to write a function:
	nbio(fd,flag)
which turns nonblocking I/O on or off depending on whether flag is true
or false.  It appears that this function must have lots of #ifdefs to 
work right.  It also appears that some manuals don't commit themselves
as to whether O_NDELAY (or O_NONBLOCK) leaves the file in a blocking or
a nonblocking state, so if you use O_NDELAY in open(), it is probably
a good idea to *always* call a function like nbio() to ensure the desired
behavior.

It might be interesting to collect together the code that does this 
job on various releases of Unix.   It should be maybe 3-6 lines of 
code on most systems.  If a few folks out there would try their hand 
at writing nbio(), testing it (;-), and sending the results to me, 
I'd be happy to post the results.  Note that this function should 
work correctly regardless of the current blocking state of the file, 
which isn't known if you've called *any* routine that you didn't write 
(or you are running on Ultrix ;-).  Any hackers out there find this 
interesting?

I wonder what the latest POSIX standard says on the topic?

-- 
Uucp: ...!{harvard.edu,ima.com,eddie.mit.edu}!minya!jc (John Chambers)
Home: 1-617-484-6393
Work: 1-508-952-3274
Cute-Saying: [I've gotta get a new one of these some day.]

guy@auspex.auspex.com (Guy Harris) (06/16/90)

>form, they won't mention the subject; anyway, the code works find on
>Ultrix, since I figured out that PMAX/Ultrix doesn't do non-blocking I/O.

I presume you mean "Ultrix doesn't turn non-blocking mode on if you open
with O_NDELAY", which is, I suspect, correct; Ultrix "started with BSD",
so it does things BSD-style.

>I can hardly wait for the Sys/5.4 release.

At which point the thing to do is probably to completely forget about
O_NDELAY and use O_NONBLOCK instead, since that's in POSIX and there is
therefore at least *some* pressure to make everybody do it the same way;
4.4BSD should also support O_NONBLOCK and do so the same way S5R4 does.

cpcahil@virtech.uucp (Conor P. Cahill) (06/17/90)

In article <409@minya.UUCP> jc@minya.UUCP (John Chambers) writes:
>
>My main problem with this is that it gets a bit clumsy as the list grows.
>Writing code that handles a variety of systems is a problem that doesn't
>seem to have been well-solved as yet.  (I expect to get lots of flames
>from the CASE crowd for that remark! ;-)

Actually it has been solved for quite a few systems/programs.  Larry Wall's
Configure program does a pretty good job at determining the capabilities
of a given system.  A few years ago (before I could spell USENET) I wrote
a similar package used to set all the #defines for an office automation
package that I was porting.  This made the porting job 10 times easier
since I no longer had to rumage around the code looking for #ifdef's with
SYSV or BSD.

The central include file that defines all the machine dependencies must
include an expanation for each #define that allows you to review it and
determine that the configuration script made the corrrect decisions.

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

gwyn@smoke.BRL.MIL (Doug Gwyn) (06/18/90)

In article <1990Jun17.123001.8299@virtech.uucp> cpcahil@virtech.UUCP (Conor P. Cahill) writes:
>Actually it has been solved for quite a few systems/programs.  Larry Wall's
>Configure program does a pretty good job at determining the capabilities
>of a given system.

It sure didn't do a very good job the few times I tried it in a mixed
(System V emulation on 4BSD) environment.  That's the point that the
fellow who suggested keying on features rather than system types was
trying to get across.  There are far too many hybrid/merged systems
now for "System V" and "BSD" labels to mean much of anything when it
comes to deciding how to configure an application.

My solution has always been to develop all applications for a viable
subset of UNIX System V no matter what the target system; one way or
another it is possible to provide the expected System V environment
for the application, which pretty much avoids having to configure it
at all.  (This approach doesn't deal with facilities not found in the
minimal supportable System V environment, such as graphics and to
some extent networking, but those are areas in which other portability
solutions than #ifdefing should be devised anyway.)