Allow libpq to do thread-safe SIGPIPE handling. This allows it to
authorBruce Momjian <bruce@momjian.us>
Fri, 9 Jan 2004 02:02:43 +0000 (02:02 +0000)
committerBruce Momjian <bruce@momjian.us>
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 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.144 2003/12/13 23:59:06 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.145 2004/01/09 02:02:43 momjian Exp $
 -->
 
  <chapter id="libpq">
@@ -3587,7 +3587,7 @@ thread-enabled applications.
 One restriction is that no two threads attempt to manipulate the same
 <structname>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.)
 </para>
 
@@ -3612,6 +3612,25 @@ not thread-safe.<indexterm><primary>crypt</><secondary>thread
 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 if no custom <literal>SIGPIPE</>
+handler has been installed previously. This handler uses thread-local
+storage to determine if a <literal>SIGPIPE</> signal has been generated
+by an internal <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>
 </sect1>
 
 
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 <ctype.h>
 #include <errno.h>
 
+#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 <arpa/inet.h>
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include <pthread.h>
+#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 <arpa/inet.h>
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include <pthread.h>
+#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 <sys/time.h>
 #endif
 
+#ifdef ENABLE_THREAD_SAFETY
+#include <pthread.h>
+#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 */