| Summary: | FreeBSD (OpenBSD) pthread implicit set/unset O_NONBLOCK flag | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Base System | Reporter: | Lars Köller <lkoeller> | ||||
| Component: | kern | Assignee: | freebsd-threads (Nobody) <threads> | ||||
| Status: | Closed FIXED | ||||||
| Severity: | Affects Only Me | ||||||
| Priority: | Normal | ||||||
| Version: | 4.9-RELEASE | ||||||
| Hardware: | Any | ||||||
| OS: | Any | ||||||
| Attachments: |
|
||||||
Responsible Changed From-To: freebsd-bugs->freebsd-threads Assign to threads developers Hi, What is the point of this PR? It contains a patch to what looks like a program in ports. The O_NONBLOCK issue with libc_r is known. Take a look at: http://www.freebsd.org/cgi/query-pr.cgi?pr=misc/41331 This problem doesn't occur with the new KSE ( http://www.freebsd.org/kse ) libpthread implementation of threads in FreeBSD 5.x. Most likely this problem will not be fixed in libc_r, since most efforts these days are focused on getting libpthread solid in 5.x. Dan Eischen has been suspending (and not closing) these kinds of PR's, i.e. problems that occur in libc_r but not in libpthread, because most likely they will not be fixed in libc_r (but they may be one day, if someone takes an interest in fixing up libc_r). Mon, Mar 15, 2004 at 21:32:43, lkoeller (Lars Koeller) wrote about "kern/64313: FreeBSD (OpenBSD) pthread implicit set/unset O_NONBLOCK flag": LKl> * The pthreads implementation under the hood sets O_NONBLOCK LKl> * implicitly on all fds. This setting is not visible to the user LKl> * application but is relied upon by the pthreads library to prevent LKl> * blocking syscalls in one thread from halting all threads in the LKl> * process. When a process exit()s or exec()s, the implicit LKl> * O_NONBLOCK flags are removed from all fds, EVEN THOSE IT INHERITED. This is unavoidable feature of libc_r. It can't detect whether O_NONBLOCK was set by itself or by another application, and can't organize thread sleeping without O_NONBLOCK. If this is critical, use another thread library (e.g. linuxthreads on 4.x, libkse or libthr on 5.x). Libc_r is unrepairable for your request (unless brain-damaged unix api with its per-opened-object O_NONBLOCK will be fixed). So your PR is meaningless and should be closed. -netch- State Changed From-To: open->suspended Incorporating text from misfiled followup kern/64349. State Changed From-To: suspended->open GNATS is one stupid program ... State Changed From-To: open->suspended Following the advice of respondants to this PR, mark this as suspended until and unless a volunteer is found who is willing to work on known libc_r issues. While this would be nice, it is unlikely, considering that 5.3 will probably mark the transition to 5-STABLE. State Changed From-To: suspended->closed libc_r is no longer supported |
/* * Work around a bug in OpenBSD & FreeBSD userspace pthreads * implementations. * * The pthreads implementation under the hood sets O_NONBLOCK * implicitly on all fds. This setting is not visible to the user * application but is relied upon by the pthreads library to prevent * blocking syscalls in one thread from halting all threads in the * process. When a process exit()s or exec()s, the implicit * O_NONBLOCK flags are removed from all fds, EVEN THOSE IT INHERITED. * If another process is still using the inherited fds, there will * soon be trouble. * * apcupsd is bitten by this issue after fork()ing a child process to * run apccontrol. * * select() is conveniently immune to the O_NONBLOCK issue so we use * that to make sure the following read() will not block. */ Fix: Don't know, I only notice the problem in the threaded apcups port which I'm the maintainer for. I receive the attached patch for the apcupsd port from Gary Bajaj <b04@interbaun.com> Originally it was written by Adam Kropelin: ################################################### ##### Attached to make the problem more clear ##### ##### See description below ##### ################################################### static int read_nbytes(int fd, char *ptr, int nbytes) { - int nleft, nread; - + int nleft, nread, rc; + +#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS) + fd_set fds; +#endif + nleft = nbytes; - errno = 0; + while (nleft > 0) { + do { + +#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS) + /* + * Work around a bug in OpenBSD & FreeBSD userspace pthreads + * implementations. + * + * The pthreads implementation under the hood sets O_NONBLOCK + * implicitly on all fds. This setting is not visible to the user + * application but is relied upon by the pthreads library to prevent + * blocking syscalls in one thread from halting all threads in the + * process. When a process exit()s or exec()s, the implicit + * O_NONBLOCK flags are removed from all fds, EVEN THOSE IT INHERITED. + * If another process is still using the inherited fds, there will + * soon be trouble. + * + * apcupsd is bitten by this issue after fork()ing a child process to + * run apccontrol. + * + * select() is conveniently immune to the O_NONBLOCK issue so we use + * that to make sure the following read() will not block. + */ + do { + FD_ZERO(&fds); + FD_SET(fd, &fds); + rc = select(fd+1, &fds, NULL, NULL, NULL); + } while (rc == -1 && (errno == EINTR || errno == EAGAIN)); + if (rc < 0) + { + net_errno = errno; + return(-1); /* error */ + } +#endif + nread = read(fd, ptr, nleft); } while (nread == -1 && (errno == EINTR || errno == EAGAIN)); if (nread <= 0) { @@ -100,6 +138,15 @@ nleft = nbytes; while (nleft > 0) { +#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS) + /* + * Work around a bug in OpenBSD & FreeBSD userspace pthreads + * implementations. Rationale is the same as described above. + * This seemingly-pointless fcntl() call causes the pthreads + * library to reapply the O_NONBLOCK flag appropriately. + */ + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)); +#endif nwritten = write(fd, ptr, nleft); if (nwritten <= 0) { net_errno = errno; @@ -225,6 +272,13 @@ return -1; } /* connect to server */ +#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS) + /* + * Work around a bug in OpenBSD & FreeBSD userspace pthreads + * implementations. Rationale is the same as described above. + */ + fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL)); +#endif if (connect(sockfd, (struct sockaddr *) &tcp_serv_addr, sizeof(tcp_serv_addr)) < 0) { sprintf(net_errbuf, "tcp_open: cannot connect to server %s on port %d.\n\ ERR=%s\n", host, port, strerror(errno)); @@ -243,6 +297,50 @@ close(sockfd); } +/* + * Accept a TCP connection. + * Returns -1 on error. + * Returns file descriptor of new connection otherwise. + */ +int net_accept(int fd, struct sockaddr_in *cli_addr) +{ + socklen_t clilen = sizeof(*cli_addr); + int newfd, rc; + +#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS) + fd_set fds; +#endif + + do { + +#if defined HAVE_PTHREADS && (defined HAVE_OPENBSD_OS || defined HAVE_FREEBSD_OS) + /* + * Work around a bug in OpenBSD & FreeBSD userspace pthreads + * implementations. Rationale is the same as described above. + */ + do { + FD_ZERO(&fds); + FD_SET(fd, &fds); + rc = select(fd+1, &fds, NULL, NULL, NULL); + } while (rc == -1 && (errno == EINTR || errno == EAGAIN)); + if (rc < 0) + { + net_errno = errno; + return(-1); /* error */ + } +#endif + + newfd = accept(fd, (struct sockaddr*)cli_addr, &clilen); + } while (newfd == -1 && (errno == EINTR || errno == EAGAIN)); + + if (newfd < 0) + { + net_errno = errno; + return(-1); /* error */ + } + + return newfd; +} int upserror, syserrno; +/* Wait for and accept a new TCP connection */ +int net_accept(int fd, struct sockaddr_in *cli_addr); + extern int upserror, syserrno;--2BesG4aDVBCRAS2YCErcC8iAXCxgFcnGyEvab7Bm4GYfJ0Zf Content-Type: text/plain; name="file.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="file.diff" --- ./src/apcnis.c Fri Jul 18 05:32:19 2003 +++ ./apcupsd-3.10.11-debug3/src/apcnis.c Fri Feb 6 21:19:14 2004 @@ -197,7 +197,6 @@ int newsockfd, sockfd, childpid; struct sockaddr_in cli_addr; /* client's address */ struct sockaddr_in serv_addr; /* our address */ - socklen_t clilen; int tlog; int turnon = 1; struct s_arg *arg; @@ -269,11 +268,7 @@ /* * Wait for a connection from a client process. */ - clilen = sizeof(cli_addr); - for (tlog=0; (newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen)) < 0; tlog -= 5*60 ) { - if (errno == EINTR) { - continue; - } + for (tlog=0; (newsockfd = net_accept(sockfd, &cli_addr)) < 0; tlog -= 5*60 ) { if (tlog <= 0) { tlog = 60*60; log_event(ups, LOG_ERR, "apcserver: accept error. ERR=%s", --- ./src/lib/apclibnis.c Sat Aug 3 18:49:45 2002 +++ ./apcupsd-3.10.11-debug3/src/lib/apclibnis.c Fri Feb 6 21:38:58 2004 @@ -71,12 +71,50 @@ How-To-Repeat: Happens sometimes in the threaded apcupsd when it calls the apccontrol script.