Allow libpq to do thread-safe SIGPIPE handling. This allows it to
authorBruce Momjian
Fri, 9 Jan 2004 02:02:43 +0000 (02:02 +0000)
committerBruce Momjian
Fri, 9 Jan 2004 02:02:43 +0000 (02:02 +0000)
ignore SIGPIPE from send() in libpq, but terminate on any other SIGPIPE,
unless the user installs their own signal handler.

This is a minor fix because the only time you get SIGPIPE from libpq's
send() is when the backend dies.

doc/src/sgml/libpq.sgml
src/backend/nodes/read.c
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
src/interfaces/libpq/pqsignal.c
src/interfaces/libpq/pqsignal.h

index 248d8646af2a6051649e2759790e854753e49add..bfd66945f9c14319ba18f6787ddbb3f0127427b0 100644 (file)
@@ -1,5 +1,5 @@
 
 
  
@@ -3587,7 +3587,7 @@ thread-enabled applications.
 One restriction is that no two threads attempt to manipulate the same
 PGconn object at the same time. In particular, you cannot
 issue concurrent commands from different threads through the same
-connection object. (If you need to run concurrent commands, start up
+connection object. (If you need to run concurrent commands, use
 multiple connections.)
 
 
@@ -3612,6 +3612,25 @@ not thread-safe.cryptthread
 safety It is better to use the md5 method,
 which is thread-safe on all platforms.
 
+
+
+libpq must ignore SIGPIPE signals
+generated internally by send() calls to backend processes.
+When PostgreSQL is configured without
+--enable-thread-safety, libpq sets
+SIGPIPE to SIG_IGN before each
+send() call and restores the original signal handler after
+completion. When --enable-thread-safety is used,
+libpq installs its own SIGPIPE handler
+before the first database connection if no custom SIGPIPE
+handler has been installed previously. This handler uses thread-local
+storage to determine if a SIGPIPE signal has been generated
+by an internal send(). If an application wants to install
+its own SIGPIPE signal handler, it should call
+PQinSend() to determine if it should ignore the
+SIGPIPE signal. This function is available in both
+thread-safe and non-thread-safe versions of libpq.
+
 
 
 
index 15abc4ac426fd46b594cf0fbc6df67fc2f59df47..7be2ff403b42119a0327bbd359f153ffab1a725f 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/read.c,v 1.37 2004/01/07 21:12:56 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/read.c,v 1.38 2004/01/09 02:02:43 momjian Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -22,6 +22,7 @@
 #include 
 #include 
 
+#include "nodes/value.h"
 #include "nodes/pg_list.h"
 #include "nodes/readfuncs.h"
 #include "nodes/value.h"
index 233d3a2fe16d9fe4ec209edd2166916e8f5d5656..1086fd76320472e648b960b04c5f50d7cd4e8a31 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.266 2004/01/07 18:56:29 neilc Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.267 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include 
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include 
+#endif
+
 #include "libpq/ip.h"
 #include "mb/pg_wchar.h"
 
@@ -66,7 +70,6 @@ long      ioctlsocket_ret=1;
 #define DefaultSSLMode "disable"
 #endif
 
-
 /* ----------
  * Definition of the conninfo parameters and their fallback resources.
  *
@@ -198,6 +201,7 @@ static char *pwdfMatchesString(char *buf, char *token);
 static char *PasswordFromFile(char *hostname, char *port, char *dbname,
                 char *username);
 
+
 /*
  *     Connecting to a Database
  *
@@ -881,6 +885,12 @@ connectDBStart(PGconn *conn)
    struct addrinfo hint;
    const char *node = NULL;
    int         ret;
+#ifdef ENABLE_THREAD_SAFETY
+   static pthread_once_t check_sigpipe_once = PTHREAD_ONCE_INIT;
+
+   /* Check only on first connection request */
+   pthread_once(&check_sigpipe_once, check_sigpipe_handler);
+#endif
 
    if (!conn)
        return 0;
@@ -3158,3 +3168,4 @@ PasswordFromFile(char *hostname, char *port, char *dbname, char *username)
 
 #undef LINELEN
 }
+
index b7fa71b1b72df7e29dacb54801712d2dcf1f62eb..9afa1294e6f3fc7d4f35988a1f3ea0cbfce678e3 100644 (file)
@@ -10,7 +10,7 @@
  * didn't really belong there.
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.49 2003/11/29 19:52:12 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.50 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,8 +90,10 @@ PQprint(FILE *fout,
        int         fs_len = strlen(po->fieldSep);
        int         total_line_length = 0;
        int         usePipe = 0;
-       pqsigfunc   oldsigpipehandler = NULL;
        char       *pagerenv;
+#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+       pqsigfunc   oldsigpipehandler = NULL;
+#endif
 
 #ifdef TIOCGWINSZ
        struct winsize screen_size;
@@ -189,8 +191,12 @@ PQprint(FILE *fout,
                if (fout)
                {
                    usePipe = 1;
+#ifdef ENABLE_THREAD_SAFETY
+                   pthread_setspecific(thread_in_send, "t");
+#else
 #ifndef WIN32
                    oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
+#endif
 #endif
                }
                else
@@ -306,7 +312,13 @@ PQprint(FILE *fout,
            _pclose(fout);
 #else
            pclose(fout);
+#endif
+#ifdef ENABLE_THREAD_SAFETY
+           pthread_setspecific(thread_in_send, "f");
+#else
+#ifndef WIN32
            pqsignal(SIGPIPE, oldsigpipehandler);
+#endif
 #endif
        }
        if (po->html3 && !po->expanded)
index 34f94b0ad0794a1192682423c2a9c558d36e7d84..e650b6b275e209eb1f69619d6ad2521608525b43 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.34 2003/12/18 22:49:26 tgl Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.35 2004/01/09 02:02:43 momjian Exp $
  *
  * NOTES
  *   The client *requires* a valid server certificate.  Since
 #include 
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include 
+#endif
+
 #ifndef HAVE_STRDUP
 #include "strdup.h"
 #endif
@@ -142,6 +146,11 @@ static const char *SSLerrmessage(void);
 static SSL_CTX *SSL_context = NULL;
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+static void sigpipe_handler_ignore_send(int signo);
+pthread_key_t thread_in_send;
+#endif
+
 /* ------------------------------------------------------------ */
 /*                      Hardcoded values                       */
 /* ------------------------------------------------------------ */
@@ -347,9 +356,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
    ssize_t     n;
 
+#ifdef ENABLE_THREAD_SAFETY
+   pthread_setspecific(thread_in_send, "t");
+#else
 #ifndef WIN32
    pqsigfunc   oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
 #endif
+#endif
 
 #ifdef USE_SSL
    if (conn->ssl)
@@ -407,8 +420,12 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 #endif
        n = send(conn->sock, ptr, len, 0);
 
+#ifdef ENABLE_THREAD_SAFETY
+   pthread_setspecific(thread_in_send, "f");
+#else
 #ifndef WIN32
    pqsignal(SIGPIPE, oldsighandler);
+#endif
 #endif
 
    return n;
@@ -1048,3 +1065,59 @@ PQgetssl(PGconn *conn)
 }
 
 #endif   /* USE_SSL */
+
+
+#ifdef ENABLE_THREAD_SAFETY
+/*
+ * Check SIGPIPE handler and perhaps install our own.
+ */
+void
+check_sigpipe_handler(void)
+{
+   pqsigfunc pipehandler;
+
+   /*
+    *  If the app hasn't set a SIGPIPE handler, define our own
+    *  that ignores SIGPIPE on libpq send() and does SIG_DFL
+    *  for other SIGPIPE cases.
+    */
+   pipehandler = pqsignalinquire(SIGPIPE);
+   if (pipehandler == SIG_DFL) /* not set by application */
+   {
+       /*
+        *  Create key first because the signal handler might be called
+        *  right after being installed.
+        */
+       pthread_key_create(&thread_in_send, NULL);  
+       pqsignal(SIGPIPE, sigpipe_handler_ignore_send);
+   }
+}
+
+/*
+ * Threaded SIGPIPE signal handler
+ */
+void
+sigpipe_handler_ignore_send(int signo)
+{
+   /* If we have gotten a SIGPIPE outside send(), exit */
+   if (!PQinSend())
+       exit(128 + SIGPIPE);    /* typical return value for SIG_DFL */
+}
+#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.
+ */
+pqbool
+PQinSend(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+   return (pthread_getspecific(thread_in_send) /* has it been set? */ &&
+           *(char *)pthread_getspecific(thread_in_send) == 't') ? true : false;
+#else
+   return false;   /* No threading, so we can't be in send() */
+#endif
+}
index 4b4eb9ab4d307f8a40216d2d7db06053604493c7..f6ea1f4ba7470051887343bb9e67f616f9c52463 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.101 2003/11/29 22:41:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.102 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -450,6 +450,14 @@ extern int PQmblen(const unsigned char *s, int encoding);
 /* Get encoding id from environment variable PGCLIENTENCODING */
 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().
+ */
+pqbool PQinSend(void);
+
 #ifdef __cplusplus
 }
 #endif
index fff2bf5d674b5b97809392cabda59984b9a32106..97cd55eaa8ad93e9ed3dfe42b2091999c444889d 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.83 2003/11/29 22:41:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.84 2004/01/09 02:02:43 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,9 @@
 #include 
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include 
+#endif
 
 #if defined(WIN32) && (!defined(ssize_t))
 typedef int ssize_t;           /* ssize_t doesn't exist in VC (at least
@@ -442,6 +445,10 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 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 check_sigpipe_handler(void);
+extern pthread_key_t thread_in_send;
+#endif
 
 /*
  * this is so that we can check if a connection is non-blocking internally
index 179a3a99aa117279c34a5c4b5bf1908597d037fb..5f41d228103be972df7765e13e595c1e5e3b45dd 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.c,v 1.18 2003/11/29 19:52:12 pgsql Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.c,v 1.19 2004/01/09 02:02:43 momjian Exp $
  *
  * NOTES
  *     This shouldn't be in libpq, but the monitor and some other
@@ -40,3 +40,25 @@ pqsignal(int signo, pqsigfunc func)
    return oact.sa_handler;
 #endif   /* !HAVE_POSIX_SIGNALS */
 }
+
+pqsigfunc
+pqsignalinquire(int signo)
+{
+#if !defined(HAVE_POSIX_SIGNALS)
+   pqsigfunc old_sigfunc;
+   int     old_sigmask;
+
+   /* Prevent signal handler calls during test */
+   old_sigmask = sigblock(sigmask(signo));
+   old_sigfunc = signal(signo, SIG_DFL);
+   signal(signo, old_sigfunc);
+   sigblock(old_sigmask);
+   return old_sigfunc;
+#else
+   struct sigaction oact;
+
+   if (sigaction(signo, NULL, &oact) < 0)
+       return SIG_ERR;
+   return oact.sa_handler;
+#endif   /* !HAVE_POSIX_SIGNALS */
+}
index 3dcb8a8b956fb8463d33f43eb1d77fef851f8223..a948802022aa4a033e918d2cdd311a9e06be8ff4 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.h,v 1.16 2003/11/29 22:41:28 pgsql Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/pqsignal.h,v 1.17 2004/01/09 02:02:43 momjian Exp $
  *
  * NOTES
  *   This shouldn't be in libpq, but the monitor and some other
@@ -24,4 +24,6 @@ typedef void (*pqsigfunc) (int);
 
 extern pqsigfunc pqsignal(int signo, pqsigfunc func);
 
+extern pqsigfunc pqsignalinquire(int signo);
+
 #endif   /* PQSIGNAL_H */