Rework libpq threaded SIGPIPE handling to avoid interference with
authorBruce Momjian <bruce@momjian.us>
Thu, 2 Dec 2004 15:32:54 +0000 (15:32 +0000)
committerBruce Momjian <bruce@momjian.us>
Thu, 2 Dec 2004 15:32:54 +0000 (15:32 +0000)
calling applications.  This is done by blocking sigpipe in the libpq
thread and using sigpending/sigwait to possibily discard any sigpipe we
generated.

configure
configure.in
doc/src/sgml/libpq.sgml
doc/src/sgml/ref/copy.sgml
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-print.c
src/interfaces/libpq/fe-secure.c
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h

index 009714367fc6eb0b102b6d08640372f8c507327b..378cd6129105a2335cfafe2fadd8fcef281734e0 100755 (executable)
--- a/configure
+++ b/configure
@@ -17431,6 +17431,18 @@ _ACEOF
 fi
 HAVE_POSIX_SIGNALS=$pgac_cv_func_posix_signals
 
+if test "$pgac_cv_func_posix_signals" != yes -a "$enable_thread_safety" = yes; then
+  { { echo "$as_me:$LINENO: error:
+*** Thread-safety requires POSIX signals, which are not supported by your
+*** operating system.
+" >&5
+echo "$as_me: error:
+*** Thread-safety requires POSIX signals, which are not supported by your
+*** operating system.
+" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
 if test $ac_cv_func_fseeko = yes; then
 # Check whether --enable-largefile or --disable-largefile was given.
 if test "${enable_largefile+set}" = set; then
index 083c3cf2c2d00ae94051319be7c04b05a2a8406a..4d289bbb6cfff20864d8143fb0b63ccff3dbcad4 100644 (file)
@@ -1174,6 +1174,13 @@ AC_CHECK_TYPES(sig_atomic_t, [], [], [#include <signal.h>])
 
 
 PGAC_FUNC_POSIX_SIGNALS
+if test "$pgac_cv_func_posix_signals" != yes -a "$enable_thread_safety" = yes; then
+  AC_MSG_ERROR([
+*** Thread-safety requires POSIX signals, which are not supported by your
+*** operating system.
+])
+fi
+
 if test $ac_cv_func_fseeko = yes; then
 AC_SYS_LARGEFILE
 fi
index 89dcba6c7bd7cd5604053ff4ac49ace599b5684d..48fcf220afd71e79ca705815bdf74eba1b6564ba 100644 (file)
@@ -3954,24 +3954,6 @@ safety</></> It is better to use the <literal>md5</literal> method,
 which is thread-safe on all platforms.
 </para>
 
-<para>
-<application>libpq</application> must ignore <literal>SIGPIPE</> signals
-generated internally by <function>send()</> calls to backend processes.
-When <productname>PostgreSQL</> is configured without
-<literal>--enable-thread-safety</>, <application>libpq</> sets
-<literal>SIGPIPE</> to <literal>SIG_IGN</> before each
-<function>send()</> call and restores the original signal handler after
-completion. When <literal>--enable-thread-safety</> is used,
-<application>libpq</> installs its own <literal>SIGPIPE</> handler
-before the first database connection.  This handler uses thread-local
-storage to determine if a <literal>SIGPIPE</> signal has been generated
-by a libpq <function>send()</>. If an application wants to install
-its own <literal>SIGPIPE</> signal handler, it should call
-<function>PQinSend()</> to determine if it should ignore the
-<literal>SIGPIPE</> signal. This function is available in both
-thread-safe and non-thread-safe versions of <application>libpq</>.
-</para>
-
 <para>
 If you experience problems with threaded applications, run
 the program in <filename>src/tools/thread</> to see if your
index 2b0a3709783e27e040c39c314525355751bbee5f..b410ac5d035c781623afdb5154620a1fb12e8478 100644 (file)
@@ -3,6 +3,7 @@ $PostgreSQL$
 PostgreSQL documentation
 -->
 
+
 <refentry id="SQL-COPY">
  <refmeta>
   <refentrytitle id="sql-copy-title">COPY</refentrytitle>
index 195324db0f9fe0b3418711f5d3386147ff8df4de..99b6416373c52866ea2341f6185f5ad52e22cb5b 100644 (file)
@@ -866,15 +866,6 @@ connectDBStart(PGconn *conn)
        const char *node = NULL;
        int                     ret;
 
-#ifdef ENABLE_THREAD_SAFETY
-#ifndef WIN32
-       static pthread_once_t check_sigpipe_once = PTHREAD_ONCE_INIT;
-
-       /* Check only on first connection request */
-       pthread_once(&check_sigpipe_once, pq_check_sigpipe_handler);
-#endif
-#endif
-
        if (!conn)
                return 0;
 
index 114d6fc3fc49393115b5abcabb45f6ccc6ffc2ce..12e0dff7511f9a753cb2f6581c8276f89c0ef654 100644 (file)
@@ -91,7 +91,11 @@ PQprint(FILE *fout,
                int                     total_line_length = 0;
                int                     usePipe = 0;
                char       *pagerenv;
-
+#ifdef ENABLE_THREAD_SAFETY
+               sigset_t        osigset;
+               bool            sigpipe_masked = false;
+               bool            sigpipe_pending;
+#endif
 #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
                pqsigfunc       oldsigpipehandler = NULL;
 #endif
@@ -189,7 +193,8 @@ PQprint(FILE *fout,
                                {
                                        usePipe = 1;
 #ifdef ENABLE_THREAD_SAFETY
-                                       pthread_setspecific(pq_thread_in_send, "t");
+                                       pq_block_sigpipe(&osigset, &sigpipe_pending);
+                                       sigpipe_masked = true;
 #else
 #ifndef WIN32
                                        oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
@@ -311,7 +316,8 @@ PQprint(FILE *fout,
                        pclose(fout);
 #endif
 #ifdef ENABLE_THREAD_SAFETY
-                       pthread_setspecific(pq_thread_in_send, "f");
+                       if (sigpipe_masked)
+                               pq_reset_sigpipe(&osigset, sigpipe_pending);
 #else
 #ifndef WIN32
                        pqsignal(SIGPIPE, oldsigpipehandler);
index ff6d529b5a9a58925ee416a70b69ec3e0e78a536..9c0a9023ecab9eb705d1ebf02f0e6d41abe825e9 100644 (file)
@@ -152,12 +152,6 @@ bool               pq_initssllib = true;
 static SSL_CTX *SSL_context = NULL;
 #endif
 
-#ifdef ENABLE_THREAD_SAFETY
-static void sigpipe_handler_ignore_send(int signo);
-pthread_key_t pq_thread_in_send = 0;   /* initializer needed on Darwin */
-static pqsigfunc pq_pipe_handler;
-#endif
-
 /* ------------------------------------------------------------ */
 /*                                              Hardcoded values                                               */
 /* ------------------------------------------------------------ */
@@ -379,9 +373,12 @@ ssize_t
 pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
        ssize_t         n;
-
+       
 #ifdef ENABLE_THREAD_SAFETY
-       pthread_setspecific(pq_thread_in_send, "t");
+       sigset_t        osigmask;
+       bool            sigpipe_pending;
+       
+       pq_block_sigpipe(&osigmask, &sigpipe_pending);
 #else
 #ifndef WIN32
        pqsigfunc       oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
@@ -452,9 +449,14 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
        else
 #endif
                n = send(conn->sock, ptr, len, 0);
+               /*
+                *      Possible optimization:  if sigpending() turns out to be an
+                *      expensive operation, we can set sigpipe_pending = 'true'
+                *      here if errno != EPIPE, avoiding a sigpending call.
+                */
 
 #ifdef ENABLE_THREAD_SAFETY
-       pthread_setspecific(pq_thread_in_send, "f");
+       pq_reset_sigpipe(&osigmask, sigpipe_pending);
 #else
 #ifndef WIN32
        pqsignal(SIGPIPE, oldsighandler);
@@ -1216,65 +1218,77 @@ PQgetssl(PGconn *conn)
 }
 #endif   /* USE_SSL */
 
-
 #ifdef ENABLE_THREAD_SAFETY
-#ifndef WIN32
 /*
- *     Check SIGPIPE handler and perhaps install our own.
+ *     Block SIGPIPE for this thread.  This prevents send()/write() from exiting
+ *     the application.
  */
-void
-pq_check_sigpipe_handler(void)
-{
-       pthread_key_create(&pq_thread_in_send, NULL);
-
-       /*
-        * Find current pipe handler and chain on to it.
-        */
-       pq_pipe_handler = pqsignalinquire(SIGPIPE);
-       pqsignal(SIGPIPE, sigpipe_handler_ignore_send);
-}
-
-/*
- *     Threaded SIGPIPE signal handler
- */
-void
-sigpipe_handler_ignore_send(int signo)
+int
+pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
 {
-       /*
-        * If we have gotten a SIGPIPE outside send(), chain or exit if we are
-        * at the end of the chain. Synchronous signals are delivered to the
-        * thread that caused the signal.
-        */
-       if (!PQinSend())
+       sigset_t sigpipe_sigset;
+       sigset_t sigset;
+       int              ret;
+       
+       sigemptyset(&sigpipe_sigset);
+       sigaddset(&sigpipe_sigset, SIGPIPE);
+
+       /* Block SIGPIPE and save previous mask for later reset */
+       ret = pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset);
+
+       /* We can have a pending SIGPIPE only if it was blocked before */
+       if (sigismember(osigset, SIGPIPE))
        {
-               if (pq_pipe_handler == SIG_DFL) /* not set by application */
-                       exit(128 + SIGPIPE);    /* typical return value for SIG_DFL */
+               /* Is there a pending SIGPIPE? */
+               if (sigpending(&sigset) != 0)
+                       return -1;
+       
+               if (sigismember(&sigset, SIGPIPE))
+                       *sigpipe_pending = true;
                else
-                       (*pq_pipe_handler) (signo); /* call original handler */
+                       *sigpipe_pending = false;
        }
+       else
+               *sigpipe_pending = false;
+       
+       return ret;
 }
-#endif
-#endif
-
+       
 /*
- *     Indicates whether the current thread is in send()
- *     For use by SIGPIPE signal handlers;  they should
- *     ignore SIGPIPE when libpq is in send().  This means
- *     that the backend has died unexpectedly.
+ *     Discard any pending SIGPIPE and reset the signal mask.
+ *     We might be discarding a blocked SIGPIPE that we didn't generate,
+ *     but we document that you can't keep blocked SIGPIPE calls across
+ *     libpq function calls.
  */
-pqbool
-PQinSend(void)
+int
+pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending)
 {
-#ifdef ENABLE_THREAD_SAFETY
-       return (pthread_getspecific(pq_thread_in_send) /* has it been set? */ &&
-                       *(char *) pthread_getspecific(pq_thread_in_send) == 't') ? true : false;
-#else
+       int     signo;
+       sigset_t sigset;
 
-       /*
-        * No threading: our code ignores SIGPIPE around send(). Therefore, we
-        * can't be in send() if we are checking from a SIGPIPE signal
-        * handler.
-        */
-       return false;
-#endif
+       /* Clear SIGPIPE only if none was pending */
+       if (!sigpipe_pending)
+       {
+               if (sigpending(&sigset) != 0)
+                       return -1;
+       
+               /*
+                *      Discard pending and blocked SIGPIPE
+                */
+               if (sigismember(&sigset, SIGPIPE))
+               {
+                       sigset_t sigpipe_sigset;
+                       
+                       sigemptyset(&sigpipe_sigset);
+                       sigaddset(&sigpipe_sigset, SIGPIPE);
+       
+                       sigwait(&sigpipe_sigset, &signo);
+                       if (signo != SIGPIPE)
+                               return -1;
+               }
+       }
+       
+       /* Restore saved block mask */
+       return pthread_sigmask(SIG_SETMASK, osigset, NULL);
 }
+#endif
index d71dd42730e02a8857430ec052b38336620e73f2..10e605d19c445b75b63f07204f1b7d6b33151c0d 100644 (file)
@@ -497,12 +497,6 @@ extern int PQenv2encoding(void);
 
 /* === in fe-secure.c === */
 
-/*
- *     Indicates whether the libpq thread is in send().
- *     Used to ignore SIGPIPE if thread is in send().
- */
-extern pqbool PQinSend(void);
-
 #ifdef __cplusplus
 }
 #endif
index 9e854a03839fb913721455393817ebd20d452789..7d024f21128ea96e1284aa63337de98949ce9f64 100644 (file)
@@ -31,6 +31,7 @@
 
 #ifdef ENABLE_THREAD_SAFETY
 #include <pthread.h>
+#include <signal.h>
 #endif
 
 #ifdef WIN32_CLIENT_ONLY
@@ -475,15 +476,15 @@ extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
 
-#ifdef ENABLE_THREAD_SAFETY
-extern void pq_check_sigpipe_handler(void);
-extern pthread_key_t pq_thread_in_send;
-#endif
-
 #ifdef USE_SSL
 extern bool pq_initssllib;
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
+int pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending);
+#endif
+
 /*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call