Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8408f65

Browse files
committed
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.
1 parent e02ef26 commit 8408f65

File tree

9 files changed

+115
-107
lines changed

9 files changed

+115
-107
lines changed

configure

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17431,6 +17431,18 @@ _ACEOF
1743117431
fi
1743217432
HAVE_POSIX_SIGNALS=$pgac_cv_func_posix_signals
1743317433

17434+
if test "$pgac_cv_func_posix_signals" != yes -a "$enable_thread_safety" = yes; then
17435+
{ { echo "$as_me:$LINENO: error:
17436+
*** Thread-safety requires POSIX signals, which are not supported by your
17437+
*** operating system.
17438+
" >&5
17439+
echo "$as_me: error:
17440+
*** Thread-safety requires POSIX signals, which are not supported by your
17441+
*** operating system.
17442+
" >&2;}
17443+
{ (exit 1); exit 1; }; }
17444+
fi
17445+
1743417446
if test $ac_cv_func_fseeko = yes; then
1743517447
# Check whether --enable-largefile or --disable-largefile was given.
1743617448
if test "${enable_largefile+set}" = set; then

configure.in

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
dnl Process this file with autoconf to produce a configure script.
2-
dnl $PostgreSQL: pgsql/configure.in,v 1.387 2004/11/30 06:13:04 tgl Exp $
2+
dnl $PostgreSQL: pgsql/configure.in,v 1.388 2004/12/02 15:32:50 momjian Exp $
33
dnl
44
dnl Developers, please strive to achieve this order:
55
dnl
@@ -1174,6 +1174,13 @@ AC_CHECK_TYPES(sig_atomic_t, [], [], [#include <signal.h>])
11741174

11751175

11761176
PGAC_FUNC_POSIX_SIGNALS
1177+
if test "$pgac_cv_func_posix_signals" != yes -a "$enable_thread_safety" = yes; then
1178+
AC_MSG_ERROR([
1179+
*** Thread-safety requires POSIX signals, which are not supported by your
1180+
*** operating system.
1181+
])
1182+
fi
1183+
11771184
if test $ac_cv_func_fseeko = yes; then
11781185
AC_SYS_LARGEFILE
11791186
fi

doc/src/sgml/libpq.sgml

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.169 2004/11/27 21:56:04 petere Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.170 2004/12/02 15:32:52 momjian Exp $
33
-->
44

55
<chapter id="libpq">
@@ -3954,24 +3954,6 @@ safety</></> It is better to use the <literal>md5</literal> method,
39543954
which is thread-safe on all platforms.
39553955
</para>
39563956

3957-
<para>
3958-
<application>libpq</application> must ignore <literal>SIGPIPE</> signals
3959-
generated internally by <function>send()</> calls to backend processes.
3960-
When <productname>PostgreSQL</> is configured without
3961-
<literal>--enable-thread-safety</>, <application>libpq</> sets
3962-
<literal>SIGPIPE</> to <literal>SIG_IGN</> before each
3963-
<function>send()</> call and restores the original signal handler after
3964-
completion. When <literal>--enable-thread-safety</> is used,
3965-
<application>libpq</> installs its own <literal>SIGPIPE</> handler
3966-
before the first database connection. This handler uses thread-local
3967-
storage to determine if a <literal>SIGPIPE</> signal has been generated
3968-
by a libpq <function>send()</>. If an application wants to install
3969-
its own <literal>SIGPIPE</> signal handler, it should call
3970-
<function>PQinSend()</> to determine if it should ignore the
3971-
<literal>SIGPIPE</> signal. This function is available in both
3972-
thread-safe and non-thread-safe versions of <application>libpq</>.
3973-
</para>
3974-
39753957
<para>
39763958
If you experience problems with threaded applications, run
39773959
the program in <filename>src/tools/thread</> to see if your

doc/src/sgml/ref/copy.sgml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.60 2004/11/27 21:56:05 petere Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/copy.sgml,v 1.61 2004/12/02 15:32:53 momjian Exp $
33
PostgreSQL documentation
44
-->
55

6+
67
<refentry id="SQL-COPY">
78
<refmeta>
89
<refentrytitle id="sql-copy-title">COPY</refentrytitle>

src/interfaces/libpq/fe-connect.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.290 2004/12/01 23:42:26 momjian Exp $
11+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.291 2004/12/02 15:32:54 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -866,15 +866,6 @@ connectDBStart(PGconn *conn)
866866
const char *node = NULL;
867867
int ret;
868868

869-
#ifdef ENABLE_THREAD_SAFETY
870-
#ifndef WIN32
871-
static pthread_once_t check_sigpipe_once = PTHREAD_ONCE_INIT;
872-
873-
/* Check only on first connection request */
874-
pthread_once(&check_sigpipe_once, pq_check_sigpipe_handler);
875-
#endif
876-
#endif
877-
878869
if (!conn)
879870
return 0;
880871

src/interfaces/libpq/fe-print.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* didn't really belong there.
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.55 2004/11/09 15:57:57 petere Exp $
13+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-print.c,v 1.56 2004/12/02 15:32:54 momjian Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -91,7 +91,11 @@ PQprint(FILE *fout,
9191
int total_line_length = 0;
9292
int usePipe = 0;
9393
char *pagerenv;
94-
94+
#ifdef ENABLE_THREAD_SAFETY
95+
sigset_t osigset;
96+
bool sigpipe_masked = false;
97+
bool sigpipe_pending;
98+
#endif
9599
#if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
96100
pqsigfunc oldsigpipehandler = NULL;
97101
#endif
@@ -189,7 +193,8 @@ PQprint(FILE *fout,
189193
{
190194
usePipe = 1;
191195
#ifdef ENABLE_THREAD_SAFETY
192-
pthread_setspecific(pq_thread_in_send, "t");
196+
pq_block_sigpipe(&osigset, &sigpipe_pending);
197+
sigpipe_masked = true;
193198
#else
194199
#ifndef WIN32
195200
oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN);
@@ -311,7 +316,8 @@ PQprint(FILE *fout,
311316
pclose(fout);
312317
#endif
313318
#ifdef ENABLE_THREAD_SAFETY
314-
pthread_setspecific(pq_thread_in_send, "f");
319+
if (sigpipe_masked)
320+
pq_reset_sigpipe(&osigset, sigpipe_pending);
315321
#else
316322
#ifndef WIN32
317323
pqsignal(SIGPIPE, oldsigpipehandler);

src/interfaces/libpq/fe-secure.c

Lines changed: 73 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.57 2004/11/20 00:35:13 tgl Exp $
14+
* $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.58 2004/12/02 15:32:54 momjian Exp $
1515
*
1616
* NOTES
1717
* [ Most of these notes are wrong/obsolete, but perhaps not all ]
@@ -152,12 +152,6 @@ bool pq_initssllib = true;
152152
static SSL_CTX *SSL_context = NULL;
153153
#endif
154154

155-
#ifdef ENABLE_THREAD_SAFETY
156-
static void sigpipe_handler_ignore_send(int signo);
157-
pthread_key_t pq_thread_in_send = 0; /* initializer needed on Darwin */
158-
static pqsigfunc pq_pipe_handler;
159-
#endif
160-
161155
/* ------------------------------------------------------------ */
162156
/* Hardcoded values */
163157
/* ------------------------------------------------------------ */
@@ -379,9 +373,12 @@ ssize_t
379373
pqsecure_write(PGconn *conn, const void *ptr, size_t len)
380374
{
381375
ssize_t n;
382-
376+
383377
#ifdef ENABLE_THREAD_SAFETY
384-
pthread_setspecific(pq_thread_in_send, "t");
378+
sigset_t osigmask;
379+
bool sigpipe_pending;
380+
381+
pq_block_sigpipe(&osigmask, &sigpipe_pending);
385382
#else
386383
#ifndef WIN32
387384
pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
@@ -452,9 +449,14 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
452449
else
453450
#endif
454451
n = send(conn->sock, ptr, len, 0);
452+
/*
453+
* Possible optimization: if sigpending() turns out to be an
454+
* expensive operation, we can set sigpipe_pending = 'true'
455+
* here if errno != EPIPE, avoiding a sigpending call.
456+
*/
455457

456458
#ifdef ENABLE_THREAD_SAFETY
457-
pthread_setspecific(pq_thread_in_send, "f");
459+
pq_reset_sigpipe(&osigmask, sigpipe_pending);
458460
#else
459461
#ifndef WIN32
460462
pqsignal(SIGPIPE, oldsighandler);
@@ -1216,65 +1218,77 @@ PQgetssl(PGconn *conn)
12161218
}
12171219
#endif /* USE_SSL */
12181220

1219-
12201221
#ifdef ENABLE_THREAD_SAFETY
1221-
#ifndef WIN32
12221222
/*
1223-
* Check SIGPIPE handler and perhaps install our own.
1223+
* Block SIGPIPE for this thread. This prevents send()/write() from exiting
1224+
* the application.
12241225
*/
1225-
void
1226-
pq_check_sigpipe_handler(void)
1227-
{
1228-
pthread_key_create(&pq_thread_in_send, NULL);
1229-
1230-
/*
1231-
* Find current pipe handler and chain on to it.
1232-
*/
1233-
pq_pipe_handler = pqsignalinquire(SIGPIPE);
1234-
pqsignal(SIGPIPE, sigpipe_handler_ignore_send);
1235-
}
1236-
1237-
/*
1238-
* Threaded SIGPIPE signal handler
1239-
*/
1240-
void
1241-
sigpipe_handler_ignore_send(int signo)
1226+
int
1227+
pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
12421228
{
1243-
/*
1244-
* If we have gotten a SIGPIPE outside send(), chain or exit if we are
1245-
* at the end of the chain. Synchronous signals are delivered to the
1246-
* thread that caused the signal.
1247-
*/
1248-
if (!PQinSend())
1229+
sigset_t sigpipe_sigset;
1230+
sigset_t sigset;
1231+
int ret;
1232+
1233+
sigemptyset(&sigpipe_sigset);
1234+
sigaddset(&sigpipe_sigset, SIGPIPE);
1235+
1236+
/* Block SIGPIPE and save previous mask for later reset */
1237+
ret = pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset);
1238+
1239+
/* We can have a pending SIGPIPE only if it was blocked before */
1240+
if (sigismember(osigset, SIGPIPE))
12491241
{
1250-
if (pq_pipe_handler == SIG_DFL) /* not set by application */
1251-
exit(128 + SIGPIPE); /* typical return value for SIG_DFL */
1242+
/* Is there a pending SIGPIPE? */
1243+
if (sigpending(&sigset) != 0)
1244+
return -1;
1245+
1246+
if (sigismember(&sigset, SIGPIPE))
1247+
*sigpipe_pending = true;
12521248
else
1253-
(*pq_pipe_handler) (signo); /* call original handler */
1249+
*sigpipe_pending = false;
12541250
}
1251+
else
1252+
*sigpipe_pending = false;
1253+
1254+
return ret;
12551255
}
1256-
#endif
1257-
#endif
1258-
1256+
12591257
/*
1260-
* Indicates whether the current thread is in send()
1261-
* For use by SIGPIPE signal handlers; they should
1262-
* ignore SIGPIPE when libpq is in send(). This means
1263-
* that the backend has died unexpectedly.
1258+
* Discard any pending SIGPIPE and reset the signal mask.
1259+
* We might be discarding a blocked SIGPIPE that we didn't generate,
1260+
* but we document that you can't keep blocked SIGPIPE calls across
1261+
* libpq function calls.
12641262
*/
1265-
pqbool
1266-
PQinSend(void)
1263+
int
1264+
pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending)
12671265
{
1268-
#ifdef ENABLE_THREAD_SAFETY
1269-
return (pthread_getspecific(pq_thread_in_send) /* has it been set? */ &&
1270-
*(char *) pthread_getspecific(pq_thread_in_send) == 't') ? true : false;
1271-
#else
1266+
int signo;
1267+
sigset_t sigset;
12721268

1273-
/*
1274-
* No threading: our code ignores SIGPIPE around send(). Therefore, we
1275-
* can't be in send() if we are checking from a SIGPIPE signal
1276-
* handler.
1277-
*/
1278-
return false;
1279-
#endif
1269+
/* Clear SIGPIPE only if none was pending */
1270+
if (!sigpipe_pending)
1271+
{
1272+
if (sigpending(&sigset) != 0)
1273+
return -1;
1274+
1275+
/*
1276+
* Discard pending and blocked SIGPIPE
1277+
*/
1278+
if (sigismember(&sigset, SIGPIPE))
1279+
{
1280+
sigset_t sigpipe_sigset;
1281+
1282+
sigemptyset(&sigpipe_sigset);
1283+
sigaddset(&sigpipe_sigset, SIGPIPE);
1284+
1285+
sigwait(&sigpipe_sigset, &signo);
1286+
if (signo != SIGPIPE)
1287+
return -1;
1288+
}
1289+
}
1290+
1291+
/* Restore saved block mask */
1292+
return pthread_sigmask(SIG_SETMASK, osigset, NULL);
12801293
}
1294+
#endif

src/interfaces/libpq/libpq-fe.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.113 2004/10/30 23:11:27 tgl Exp $
10+
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-fe.h,v 1.114 2004/12/02 15:32:54 momjian Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -497,12 +497,6 @@ extern int PQenv2encoding(void);
497497

498498
/* === in fe-secure.c === */
499499

500-
/*
501-
* Indicates whether the libpq thread is in send().
502-
* Used to ignore SIGPIPE if thread is in send().
503-
*/
504-
extern pqbool PQinSend(void);
505-
506500
#ifdef __cplusplus
507501
}
508502
#endif

src/interfaces/libpq/libpq-int.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
1313
* Portions Copyright (c) 1994, Regents of the University of California
1414
*
15-
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.96 2004/10/30 23:11:27 tgl Exp $
15+
* $PostgreSQL: pgsql/src/interfaces/libpq/libpq-int.h,v 1.97 2004/12/02 15:32:54 momjian Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
@@ -31,6 +31,7 @@
3131

3232
#ifdef ENABLE_THREAD_SAFETY
3333
#include <pthread.h>
34+
#include <signal.h>
3435
#endif
3536

3637
#ifdef WIN32_CLIENT_ONLY
@@ -475,15 +476,15 @@ extern void pqsecure_close(PGconn *);
475476
extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
476477
extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
477478

478-
#ifdef ENABLE_THREAD_SAFETY
479-
extern void pq_check_sigpipe_handler(void);
480-
extern pthread_key_t pq_thread_in_send;
481-
#endif
482-
483479
#ifdef USE_SSL
484480
extern bool pq_initssllib;
485481
#endif
486482

483+
#ifdef ENABLE_THREAD_SAFETY
484+
int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
485+
int pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending);
486+
#endif
487+
487488
/*
488489
* this is so that we can check if a connection is non-blocking internally
489490
* without the overhead of a function call

0 commit comments

Comments
 (0)