Bug 64313

Summary: FreeBSD (OpenBSD) pthread implicit set/unset O_NONBLOCK flag
Product: Base System Reporter: Lars Köller <lkoeller>
Component: kernAssignee: freebsd-threads (Nobody) <threads>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.9-RELEASE   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description Lars Köller 2004-03-15 20:40:21 UTC
            /*
	     * 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.
Comment 1 Kris Kennaway freebsd_committer freebsd_triage 2004-03-16 00:33:11 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-threads

Assign to threads developers
Comment 2 Craig Rodrigues 2004-03-16 18:10:53 UTC
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).
Comment 3 netch 2004-03-21 15:04:41 UTC
 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-
Comment 4 Mark Linimon freebsd_committer freebsd_triage 2004-03-29 18:15:36 UTC
State Changed
From-To: open->suspended

Incorporating text from misfiled followup kern/64349.
Comment 5 Mark Linimon freebsd_committer freebsd_triage 2004-03-29 18:16:56 UTC
State Changed
From-To: suspended->open

GNATS is one stupid program ...
Comment 6 Mark Linimon freebsd_committer freebsd_triage 2004-03-29 18:17:37 UTC
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.
Comment 7 K. Macy freebsd_committer freebsd_triage 2007-11-16 01:15:27 UTC
State Changed
From-To: suspended->closed


libc_r is no longer supported