Commit 8408f652 authored by Bruce Momjian's avatar Bruce Momjian

Rework libpq threaded SIGPIPE handling to avoid interference with

calling applications.  This is done by blocking sigpipe in the libpq
thread and using sigpending/sigwait to possibily discard any sigpipe we
generated.
parent e02ef267
...@@ -17431,6 +17431,18 @@ _ACEOF ...@@ -17431,6 +17431,18 @@ _ACEOF
fi fi
HAVE_POSIX_SIGNALS=$pgac_cv_func_posix_signals 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 if test $ac_cv_func_fseeko = yes; then
# Check whether --enable-largefile or --disable-largefile was given. # Check whether --enable-largefile or --disable-largefile was given.
if test "${enable_largefile+set}" = set; then if test "${enable_largefile+set}" = set; then
......
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
dnl $PostgreSQL: pgsql/configure.in,v 1.387 2004/11/30 06:13:04 tgl Exp $ dnl $PostgreSQL: pgsql/configure.in,v 1.388 2004/12/02 15:32:50 momjian Exp $
dnl dnl
dnl Developers, please strive to achieve this order: dnl Developers, please strive to achieve this order:
dnl dnl
...@@ -1174,6 +1174,13 @@ AC_CHECK_TYPES(sig_atomic_t, [], [], [#include <signal.h>]) ...@@ -1174,6 +1174,13 @@ AC_CHECK_TYPES(sig_atomic_t, [], [], [#include <signal.h>])
PGAC_FUNC_POSIX_SIGNALS 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 if test $ac_cv_func_fseeko = yes; then
AC_SYS_LARGEFILE AC_SYS_LARGEFILE
fi fi
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.169 2004/11/27 21:56:04 petere Exp $ $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.170 2004/12/02 15:32:52 momjian Exp $
--> -->
<chapter id="libpq"> <chapter id="libpq">
...@@ -3954,24 +3954,6 @@ safety</></> It is better to use the <literal>md5</literal> method, ...@@ -3954,24 +3954,6 @@ safety</></> It is better to use the <literal>md5</literal> method,
which is thread-safe on all platforms. which is thread-safe on all platforms.
</para> </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> <para>
If you experience problems with threaded applications, run If you experience problems with threaded applications, run
the program in <filename>src/tools/thread</> to see if your the program in <filename>src/tools/thread</> to see if your
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.60 2004/11/27 21:56:05 petere Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.61 2004/12/02 15:32:53 momjian Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
<refentry id="SQL-COPY"> <refentry id="SQL-COPY">
<refmeta> <refmeta>
<refentrytitle id="sql-copy-title">COPY</refentrytitle> <refentrytitle id="sql-copy-title">COPY</refentrytitle>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.290 2004/12/01 23:42:26 momjian Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.291 2004/12/02 15:32:54 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -866,15 +866,6 @@ connectDBStart(PGconn *conn) ...@@ -866,15 +866,6 @@ connectDBStart(PGconn *conn)
const char *node = NULL; const char *node = NULL;
int ret; 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) if (!conn)
return 0; return 0;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* didn't really belong there. * didn't really belong there.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.55 2004/11/09 15:57:57 petere Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.56 2004/12/02 15:32:54 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -91,7 +91,11 @@ PQprint(FILE *fout, ...@@ -91,7 +91,11 @@ PQprint(FILE *fout,
int total_line_length = 0; int total_line_length = 0;
int usePipe = 0; int usePipe = 0;
char *pagerenv; char *pagerenv;
#ifdef ENABLE_THREAD_SAFETY
sigset_t osigset;
bool sigpipe_masked = false;
bool sigpipe_pending;
#endif
#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
pqsigfunc oldsigpipehandler = NULL; pqsigfunc oldsigpipehandler = NULL;
#endif #endif
...@@ -189,7 +193,8 @@ PQprint(FILE *fout, ...@@ -189,7 +193,8 @@ PQprint(FILE *fout,
{ {
usePipe = 1; usePipe = 1;
#ifdef ENABLE_THREAD_SAFETY #ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(pq_thread_in_send, "t"); pq_block_sigpipe(&osigset, &sigpipe_pending);
sigpipe_masked = true;
#else #else
#ifndef WIN32 #ifndef WIN32
oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN); oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
...@@ -311,7 +316,8 @@ PQprint(FILE *fout, ...@@ -311,7 +316,8 @@ PQprint(FILE *fout,
pclose(fout); pclose(fout);
#endif #endif
#ifdef ENABLE_THREAD_SAFETY #ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(pq_thread_in_send, "f"); if (sigpipe_masked)
pq_reset_sigpipe(&osigset, sigpipe_pending);
#else #else
#ifndef WIN32 #ifndef WIN32
pqsignal(SIGPIPE, oldsigpipehandler); pqsignal(SIGPIPE, oldsigpipehandler);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.57 2004/11/20 00:35:13 tgl Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.58 2004/12/02 15:32:54 momjian Exp $
* *
* NOTES * NOTES
* [ Most of these notes are wrong/obsolete, but perhaps not all ] * [ Most of these notes are wrong/obsolete, but perhaps not all ]
...@@ -152,12 +152,6 @@ bool pq_initssllib = true; ...@@ -152,12 +152,6 @@ bool pq_initssllib = true;
static SSL_CTX *SSL_context = NULL; static SSL_CTX *SSL_context = NULL;
#endif #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 */ /* Hardcoded values */
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
...@@ -379,9 +373,12 @@ ssize_t ...@@ -379,9 +373,12 @@ ssize_t
pqsecure_write(PGconn *conn, const void *ptr, size_t len) pqsecure_write(PGconn *conn, const void *ptr, size_t len)
{ {
ssize_t n; ssize_t n;
#ifdef ENABLE_THREAD_SAFETY #ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(pq_thread_in_send, "t"); sigset_t osigmask;
bool sigpipe_pending;
pq_block_sigpipe(&osigmask, &sigpipe_pending);
#else #else
#ifndef WIN32 #ifndef WIN32
pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN); pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
...@@ -452,9 +449,14 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len) ...@@ -452,9 +449,14 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
else else
#endif #endif
n = send(conn->sock, ptr, len, 0); 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 #ifdef ENABLE_THREAD_SAFETY
pthread_setspecific(pq_thread_in_send, "f"); pq_reset_sigpipe(&osigmask, sigpipe_pending);
#else #else
#ifndef WIN32 #ifndef WIN32
pqsignal(SIGPIPE, oldsighandler); pqsignal(SIGPIPE, oldsighandler);
...@@ -1216,65 +1218,77 @@ PQgetssl(PGconn *conn) ...@@ -1216,65 +1218,77 @@ PQgetssl(PGconn *conn)
} }
#endif /* USE_SSL */ #endif /* USE_SSL */
#ifdef ENABLE_THREAD_SAFETY #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 int
pq_check_sigpipe_handler(void) pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
{
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)
{ {
/* sigset_t sigpipe_sigset;
* If we have gotten a SIGPIPE outside send(), chain or exit if we are sigset_t sigset;
* at the end of the chain. Synchronous signals are delivered to the int ret;
* thread that caused the signal.
*/ sigemptyset(&sigpipe_sigset);
if (!PQinSend()) 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 */ /* Is there a pending SIGPIPE? */
exit(128 + SIGPIPE); /* typical return value for SIG_DFL */ if (sigpending(&sigset) != 0)
return -1;
if (sigismember(&sigset, SIGPIPE))
*sigpipe_pending = true;
else 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() * Discard any pending SIGPIPE and reset the signal mask.
* For use by SIGPIPE signal handlers; they should * We might be discarding a blocked SIGPIPE that we didn't generate,
* ignore SIGPIPE when libpq is in send(). This means * but we document that you can't keep blocked SIGPIPE calls across
* that the backend has died unexpectedly. * libpq function calls.
*/ */
pqbool int
PQinSend(void) pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending)
{ {
#ifdef ENABLE_THREAD_SAFETY int signo;
return (pthread_getspecific(pq_thread_in_send) /* has it been set? */ && sigset_t sigset;
*(char *) pthread_getspecific(pq_thread_in_send) == 't') ? true : false;
#else
/* /* Clear SIGPIPE only if none was pending */
* No threading: our code ignores SIGPIPE around send(). Therefore, we if (!sigpipe_pending)
* can't be in send() if we are checking from a SIGPIPE signal {
* handler. if (sigpending(&sigset) != 0)
*/ return -1;
return false;
#endif /*
* 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
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.113 2004/10/30 23:11:27 tgl Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.114 2004/12/02 15:32:54 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -497,12 +497,6 @@ extern int PQenv2encoding(void); ...@@ -497,12 +497,6 @@ extern int PQenv2encoding(void);
/* === in fe-secure.c === */ /* === 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 #ifdef __cplusplus
} }
#endif #endif
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.96 2004/10/30 23:11:27 tgl Exp $ * $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.97 2004/12/02 15:32:54 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#ifdef ENABLE_THREAD_SAFETY #ifdef ENABLE_THREAD_SAFETY
#include <pthread.h> #include <pthread.h>
#include <signal.h>
#endif #endif
#ifdef WIN32_CLIENT_ONLY #ifdef WIN32_CLIENT_ONLY
...@@ -475,15 +476,15 @@ extern void pqsecure_close(PGconn *); ...@@ -475,15 +476,15 @@ extern void pqsecure_close(PGconn *);
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len); extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
extern ssize_t pqsecure_write(PGconn *, const 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 #ifdef USE_SSL
extern bool pq_initssllib; extern bool pq_initssllib;
#endif #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 * this is so that we can check if a connection is non-blocking internally
* without the overhead of a function call * without the overhead of a function call
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment