From 7be759719d858c9119e77cd06c0337cd5324d558 Mon Sep 17 00:00:00 2001
From: Rich Megginson <rmeggins@redhat.com>
Date: Tue, 21 Apr 2015 10:59:13 -0600
Subject: [PATCH] Ticket #48164 use non-blocking io for event queue notify

https://fedorahosted.org/389/ticket/48164
Reviewed by: ???
Branch: master
Fix Description: NSPR pipes created with PR_CreatePipe do not support
the use of PR_SetSocketOption, so just use fcntl on the actual fds to
set to use non-blocking I/O.  Also, don't use PR_Read/PR_Write on the
NSPR PRFDs, use read/write on the underlying sockets.  Also have to
use errno to look for blocking state and error reporting.
Platforms tested: Fedora 20, RHEL 7
Flag Day: no
Doc impact: no
---
 config.h.in  |  12 +++++
 configure    | 142 ++++++++++++++++++++++++++++++++---------------------------
 configure.ac |   4 +-
 ns_thrpool.c |  57 +++++++++++++++++++-----
 4 files changed, 138 insertions(+), 77 deletions(-)

diff --git a/config.h.in b/config.h.in
index 1e84a3f..97a9ffa 100644
--- a/config.h.in
+++ b/config.h.in
@@ -3,6 +3,12 @@
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
 
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
 /* Define to 1 if you have the <inttypes.h> header file. */
 #undef HAVE_INTTYPES_H
 
@@ -25,9 +31,15 @@
 /* Define to 1 if you have the <string.h> header file. */
 #undef HAVE_STRING_H
 
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
 /* Define to 1 if you have the <sys/stat.h> header file. */
 #undef HAVE_SYS_STAT_H
 
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
 /* Define to 1 if you have the <sys/types.h> header file. */
 #undef HAVE_SYS_TYPES_H
 
diff --git a/configure b/configure
index 770c740..746643b 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for nunc-stans 0.1.0.
+# Generated by GNU Autoconf 2.69 for nunc-stans 0.1.1.
 #
 # Report bugs to <http://fedorahosted.org/389>.
 #
@@ -590,8 +590,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='nunc-stans'
 PACKAGE_TARNAME='nunc-stans'
-PACKAGE_VERSION='0.1.0'
-PACKAGE_STRING='nunc-stans 0.1.0'
+PACKAGE_VERSION='0.1.1'
+PACKAGE_STRING='nunc-stans 0.1.1'
 PACKAGE_BUGREPORT='http://fedorahosted.org/389'
 PACKAGE_URL=''
 
@@ -1334,7 +1334,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures nunc-stans 0.1.0 to adapt to many kinds of systems.
+\`configure' configures nunc-stans 0.1.1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1404,7 +1404,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of nunc-stans 0.1.0:";;
+     short | recursive ) echo "Configuration of nunc-stans 0.1.1:";;
    esac
   cat <<\_ACEOF
 
@@ -1523,7 +1523,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-nunc-stans configure 0.1.0
+nunc-stans configure 0.1.1
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1798,60 +1798,6 @@ $as_echo "$ac_res" >&6; }
 
 } # ac_fn_c_check_func
 
-# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
-# -------------------------------------------
-# Tests whether TYPE exists after having included INCLUDES, setting cache
-# variable VAR accordingly.
-ac_fn_c_check_type ()
-{
-  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  eval "$3=no"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-if (sizeof ($2))
-	 return 0;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-if (sizeof (($2)))
-	    return 0;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-
-else
-  eval "$3=yes"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-eval ac_res=\$$3
-	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-
-} # ac_fn_c_check_type
-
 # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
 # -------------------------------------------------------
 # Tests whether HEADER exists, giving a warning if it cannot be compiled using
@@ -1943,6 +1889,60 @@ fi
 
 } # ac_fn_c_check_header_mongrel
 
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+	 return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+	    return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
 # ac_fn_c_find_uintX_t LINENO BITS VAR
 # ------------------------------------
 # Finds an unsigned integer type with width BITS, setting cache variable VAR
@@ -2000,7 +2000,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by nunc-stans $as_me 0.1.0, which was
+It was created by nunc-stans $as_me 0.1.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2863,7 +2863,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='nunc-stans'
- VERSION='0.1.0'
+ VERSION='0.1.1'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -12226,7 +12226,19 @@ fi
 
 
 # Checks for header files.
-#AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h])
+for ac_header in fcntl.h stdlib.h string.h sys/time.h syslog.h unistd.h errno.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
 
 # Checks for typedefs, structures, and compiler characteristics.
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
@@ -13436,7 +13448,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by nunc-stans $as_me 0.1.0, which was
+This file was extended by nunc-stans $as_me 0.1.1, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -13502,7 +13514,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-nunc-stans config.status 0.1.0
+nunc-stans config.status 0.1.1
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index 067c86f..6c7392c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
 #                                               -*- Autoconf -*-
 # Process this file with autoconf to produce a configure script.
 AC_PREREQ(2.69)
-AC_INIT([nunc-stans], [0.1.0], [http://fedorahosted.org/389])
+AC_INIT([nunc-stans], [0.1.1], [http://fedorahosted.org/389])
 AM_INIT_AUTOMAKE([1.9 foreign subdir-objects])
 AC_CONFIG_HEADERS([config.h])
 AM_MAINTAINER_MODE
@@ -16,7 +16,7 @@ AC_PROG_LN_S
 AC_PROG_MAKE_SET
 
 # Checks for header files.
-#AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h unistd.h])
+AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/time.h syslog.h unistd.h errno.h])
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
diff --git a/ns_thrpool.c b/ns_thrpool.c
index 0c3a0b6..42cac3d 100644
--- a/ns_thrpool.c
+++ b/ns_thrpool.c
@@ -2,12 +2,24 @@
 #  include <config.h>
 #endif
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
 #include "nspr.h"
 #include "pratom.h"
 #include "private/pprio.h"
 #include "liblfds.h"
 #include "ns_event_fw.h"
-#include <syslog.h>
 
 /*
  * Threadpool
@@ -46,6 +58,7 @@ struct ns_thread_t {
 };
 
 #define ATOMIC_VAL_IS_ZERO(val) 0 == PR_AtomicAdd((val), 0)
+#define ERRNO_WOULD_BLOCK(iii) (iii == EWOULDBLOCK) || (iii == EAGAIN)
 
 /* logging function pointers */
 static void (*logger)(int, const char *, va_list) = NULL;
@@ -232,11 +245,17 @@ event_q_wake(ns_thrpool_t *tp)
 {
     PRInt32 len;
 
-    len = PR_Write(tp->event_q_wakeup_pipe_write, "a", 1);
+    /* NSPR I/O doesn't allow non-blocking signal pipes, so use write instead of PR_Write */
+    len = write(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_write),
+                "a", 1);
     if (1 != len) {
-        PRErrorCode prerr = PR_GetError();
-        ns_log(LOG_ERR, "Error: could not write wakeup pipe: %d:%s\n",
-               prerr, PR_ErrorToString(prerr, PR_LANGUAGE_I_DEFAULT));
+        if ((errno == 0) || ERRNO_WOULD_BLOCK(errno)) {
+            ns_log(LOG_DEBUG, "Write blocked for wakeup pipe - ignore %d\n",
+                   errno);
+        } else {
+            ns_log(LOG_ERR, "Error: could not write wakeup pipe: %d:%s\n",
+                   errno, PR_ErrorToString(errno, PR_LANGUAGE_I_DEFAULT));
+        }
     }
     PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield to allow event thread to pick up event */
 }
@@ -335,11 +354,17 @@ wakeup_cb(ns_job_t *job)
     PRInt32 len;
     char buf[1];
 
-    len = PR_Read(job->tp->event_q_wakeup_pipe_read, buf, 1);
+    /* NSPR I/O doesn't allow non-blocking signal pipes, so use read instead of PR_Read */
+    len = read(PR_FileDesc2NativeHandle(job->tp->event_q_wakeup_pipe_read),
+               buf, 1);
     if (1 != len) {
-        PRErrorCode prerr = PR_GetError();
-        ns_log(LOG_ERR, "Error: could not read wakeup pipe: %d:%s\n",
-               prerr, PR_ErrorToString(prerr, PR_LANGUAGE_I_DEFAULT));
+        if ((errno == 0) || ERRNO_WOULD_BLOCK(errno)) {
+            ns_log(LOG_DEBUG, "Read blocked for wakeup pipe - ignore %d\n",
+                   errno);
+        } else {
+            ns_log(LOG_ERR, "Error: could not read wakeup pipe: %d:%s\n",
+                   errno, PR_ErrorToString(errno, PR_LANGUAGE_I_DEFAULT));
+        }
     }
     /* wakeup_cb is usually called because a worker thread has posted a new
        event we need to add - get all new event requests */
@@ -669,11 +694,23 @@ static void
 setup_event_q_wakeup(ns_thrpool_t *tp)
 {
     ns_job_t *job;
-    PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
     PR_CreatePipe(&tp->event_q_wakeup_pipe_read,
                   &tp->event_q_wakeup_pipe_write);
+    /* setting options is not supported on NSPR pipes - use fcntl
+    PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
     PR_SetSocketOption(tp->event_q_wakeup_pipe_read, &prsod);
     PR_SetSocketOption(tp->event_q_wakeup_pipe_write, &prsod);
+    */
+    if (fcntl(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_read),
+              F_SETFD, O_NONBLOCK) == -1) {
+        ns_log(LOG_ERR, "setup_event_q_wakeup(): could not make read pipe non-blocking: %d\n",
+               PR_GetOSError());
+    }
+    if (fcntl(PR_FileDesc2NativeHandle(tp->event_q_wakeup_pipe_write),
+              F_SETFD, O_NONBLOCK) == -1) {
+        ns_log(LOG_ERR, "setup_event_q_wakeup(): could not make write pipe non-blocking: %d\n",
+               PR_GetOSError());
+    }
     /* wakeup events are processed inside the event loop thread */
     job = alloc_io_context(tp, tp->event_q_wakeup_pipe_read,
                             NS_JOB_READ|NS_JOB_PERSIST|NS_JOB_PRESERVE_FD,
-- 
1.9.3

