Subject: kern/31572: [dM] tty MIN>1 TIME>0 O_NONBLOCK spins
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: der Mouse <mouse@Rodents.Montreal.QC.CA>
List: netbsd-bugs
Date: 10/13/2005 03:59:01
>Number: 31572
>Category: kern
>Synopsis: When MIN>1 TIME>0, poll() on tty succeeds but read EAGAINs
>Confidential: no
>Severity: serious
>Priority: low
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Oct 13 03:59:00 +0000 2005
>Originator: der Mouse
>Release: NetBSD 2.0, also present in 1.4T, probably others
>Organization:
Dis-
>Environment:
First seen on 1.4T sparc. Tested present on 2.0 i386. Code
inspection makes me suspect it's still present in -current.
>Description:
On a tty (tested only on pseudo-ttys, but code inspection makes
me think it applies to all ttys), when c_cc[VMIN] > 1 and
c_cc[VTIME] > 0 (and ICANON is off), and non-blocking I/O is
turned on, then poll() returns indicating readable as soon as
one character is typed, but a read returns showing EWOULDBLOCK
until VMIN characters have been received. (Presumably if
non-blocking I/O were turned off, read would block instead,
which is also wrong but not quite as noisily so.)
I've marked this as serious/low, serious because it is fairly
serious for poll and read to disagree like this, but low
because it obviously doesn't break much or it would have been
found and fixed long ago.
>How-To-Repeat:
Try the above conditions. Here's a test program. Compile it
(no particular options needed for me on 2.0 i386 or 1.4T sparc)
and run it with no arguments. Type a character and watch the
flood of dots (type a second character to end it; interrupting
it may leave your tty state hosed).
#include <poll.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <strings.h>
extern const char *__progname;
static struct termios tio_o;
static struct termios tio_n;
static void restore(void)
{
tcsetattr(0,TCSANOW,&tio_o);
fcntl(0,F_SETFL,fcntl(0,F_GETFL,0)&~O_NONBLOCK);
}
int main(void);
int main(void)
{
int i;
int e;
char ch[2];
if (tcgetattr(0,&tio_o) < 0)
{ fprintf(stderr,"%s: tcgetattr: %s\n",__progname,strerror(errno));
exit(1);
}
tio_n = tio_o;
cfmakeraw(&tio_n);
tio_n.c_cc[VMIN] = 2;
tio_n.c_cc[VTIME] = 1;
fcntl(0,F_SETFL,fcntl(0,F_GETFL,0)|O_NONBLOCK);
printf("\
Test ends as soon as you type two characters. Each dot printed indicates\n\
poll() succeeded but read() returned EWOULDBLOCK (this shouldn't happen).\n");
tcsetattr(0,TCSADRAIN,&tio_n);
while (1)
{ struct pollfd pfd;
pfd.fd = 0;
pfd.events = POLLIN | POLLRDNORM;
i = poll(&pfd,1,INFTIM);
if (i < 0)
{ switch (errno)
{ case EWOULDBLOCK: case EINTR: continue; break;
}
e = errno;
restore();
fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno));
exit(1);
}
i = read(0,&ch[0],2);
if (i < 0)
{ switch (errno)
{ case EWOULDBLOCK:
write(1,".",1);
continue;
break;
case EINTR:
continue;
break;
}
e = errno;
restore();
fprintf(stderr,"%s: read: %s\n",__progname,strerror(errno));
exit(1);
}
if (i != 2)
{ restore();
fprintf(stderr,"%s: read: wanted 2, got %d\n",__progname,i);
exit(1);
}
restore();
exit(0);
}
}
>Fix:
Workaround: when doing raw-style I/O, always set c_cc[VMIN] to
1 and/or c_cc[VTIME] to 0. (These should always be set to
something; values in violation of this are not unreasonable
when doing time-limited record-style input.)
Fix: I speculate that ttpoll() needs a check more complicated
than "ttnread(tp) > 0" in the POLLIN|POLLRDNORM case, so it
doesn't misfire in cases when ttread() would sleep. However, I
have not even designed such a check, much less tested one, so
this is speculation.
/~\ The ASCII der Mouse
\ / Ribbon Campaign
X Against HTML mouse@rodents.montreal.qc.ca
/ \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B