Allow libpq to do thread-safe SIGPIPE handling. This allows it to

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.
This commit is contained in:
Bruce Momjian 2004-01-09 02:02:43 +00:00
parent acc57543de
commit 0150dbdce5
9 changed files with 167 additions and 12 deletions

View 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>

View 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"

View 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 $
*
*-------------------------------------------------------------------------
*/
@ -43,6 +43,10 @@
#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
}

View 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)

View 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
@ -106,6 +106,10 @@
#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
}

View 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

View 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

View 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 */
}

View 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 */