From owner-freebsd-arch@FreeBSD.ORG Tue Apr 8 09:47:51 2003 Return-Path: Delivered-To: freebsd-arch@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 3206B37B401 for ; Tue, 8 Apr 2003 09:47:51 -0700 (PDT) Received: from comp.chem.msu.su (comp-ext.chem.msu.su [158.250.32.157]) by mx1.FreeBSD.org (Postfix) with ESMTP id 9517A43FAF for ; Tue, 8 Apr 2003 09:47:45 -0700 (PDT) (envelope-from yar@comp.chem.msu.su) Received: from comp.chem.msu.su (localhost [127.0.0.1]) by comp.chem.msu.su (8.12.3p2/8.12.3) with ESMTP id h38GkFhE010798 for ; Tue, 8 Apr 2003 20:47:39 +0400 (MSD) (envelope-from yar@comp.chem.msu.su) Received: (from yar@localhost) by comp.chem.msu.su (8.12.3p2/8.12.3/Submit) id h38GkFXq010797 for arch@freebsd.org; Tue, 8 Apr 2003 20:46:15 +0400 (MSD) (envelope-from yar) Date: Tue, 8 Apr 2003 20:46:14 +0400 From: Yar Tikhiy To: arch@freebsd.org Message-ID: <20030408164614.GA7236@comp.chem.msu.su> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.3i Subject: termios & non-blocking I/O X-BeenThere: freebsd-arch@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Discussion related to FreeBSD architecture List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 08 Apr 2003 16:47:51 -0000 Hello everybody, I was shown a curious feature of FreeBSD. (The source of a test program illustrating it is attached at the the of this message.) Let's consider a non-blocking file descriptor that correspons to a terminal in raw mode. Let's also assume read(2) is issued on it when there is no data to read. If for this terminal MIN > 0 and TIME == 0, read(2) will return -1 and set errno to EAGAIN. OTOH, if MIN == 0 and TIME > 0, read(2) will return 0. While not in disagreement with POSIX[1], such a behaviour has at least one unwelcome consequence: If a program has been compiled with ``-pthread'', the TIME counter won't work on terminal descriptors that are in blocking mode from the program's point of view -- read(2) will instantly return 0 on them. That is because the following scenario will happen: 1) libc_r sets non-blocking mode on a descriptor as soon as a device is opened (that is how i/o in user-land threads work); 2) the program sets the TIME counter through tcsetattr(3); 3) the program issues read(2), which ends up in the actual read() syscall, which in turn returns 0 to libc_r (assuming there is no data to read); 4) libc_r thinks this is the EOF indicator, so it instantly returns 0 to the program; 5) the program breaks. Notice, that MIN works right with libc_r since read() syscall will return -1/EAGAIN, which is correctly understood by libc_r: it will block the current thread until there is data to read. Shouldn't both TIME and MIN cases be uniform in returning -1/EAGAIN on non-blocking descriptors? References: [1] IEEE Std 1003.1, 2003 Edition, General Terminal Interface, Non-Canonical Mode Input Processing. http://www.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap11.html -- Yar =========================== cut here ============================== #include #include #include #include #include #include #include #include #include #include int main(int argc, char **argv) { int c, fd, flags, i, n; int nblock = 0; int vmin = 0, vtime = 0; struct termios ts; char *port = "/dev/cuaa1"; char buf[256]; while ((c = getopt(argc, argv, "f:hm:nt:")) != -1) switch (c) { case 'f': port = optarg; break; case 'm': vmin = atoi(optarg); break; case 'n': nblock = 1; break; case 't': vtime = atoi(optarg); break; default: fprintf(stderr, "usage: termtest [-n] [-f special] [-m MIN] [-t TIME]\n"); fprintf(stderr, "\t-n -- turn on non-blocking mode explicitly\n"); fprintf(stderr, "\t-f -- specify a device to use\n"); fprintf(stderr, "\t-m -- set MIN value\n"); fprintf(stderr, "\t-t -- set TIME value (in 0.1 sec units)\n"); exit(2); } if ((fd = open(port, O_RDWR | O_NOCTTY)) == -1) err(2, "open"); if (nblock) { if ((flags = fcntl(fd, F_GETFL, 0)) == -1) err(2, "getfl"); flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) err(2, "setfl"); } if (tcgetattr(fd, &ts) == -1) err(2, "tcgetattr"); cfmakeraw(&ts); if (cfsetspeed(&ts, B9600) == -1) err(2, "cfsetspeed"); ts.c_cflag |= CLOCAL; ts.c_cflag &= ~(PARENB | PARODD); ts.c_cflag &= ~CSIZE; ts.c_cflag |= CS8; ts.c_cflag &= ~CSTOPB; ts.c_cc[VMIN] = vmin; ts.c_cc[VTIME] = vtime; if (tcsetattr(fd, TCSAFLUSH, &ts) == -1) err(2, "tcsetattr"); n = read(fd, buf, sizeof(buf)); if (n == -1) err(2, "read"); else if (n < 1) errx(2, "short read: %d bytes", n); printf("Read %d bytes:\n", n); for (i = 0; i < n; i++) printf("%#04x - %c\n", buf[i], isprint(buf[i]) ? buf[i] : '?'); close(fd); return (0); }