diff options
author | Bruce Momjian <bruce@momjian.us> | 2004-12-02 15:32:54 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 2004-12-02 15:32:54 +0000 |
commit | 8408f65252c23040e766b6a3ecde7bff390e2847 (patch) | |
tree | 4db202b3d10eab659e3d8c9805691523c9a5555e /src | |
parent | e02ef267fc3d41cbba41b2bf404d18a936d28691 (diff) | |
download | postgresql-8408f65252c23040e766b6a3ecde7bff390e2847.tar.gz postgresql-8408f65252c23040e766b6a3ecde7bff390e2847.zip |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 11 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-print.c | 14 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-secure.c | 132 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-fe.h | 8 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 13 |
5 files changed, 92 insertions, 86 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 6da2c79af4a..30e56b4b6eb 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * 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) const char *node = NULL; 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) return 0; diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c index 832af2b3ac0..9992de533d7 100644 --- a/src/interfaces/libpq/fe-print.c +++ b/src/interfaces/libpq/fe-print.c @@ -10,7 +10,7 @@ * didn't really belong there. * * 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, int total_line_length = 0; int usePipe = 0; char *pagerenv; - +#ifdef ENABLE_THREAD_SAFETY + sigset_t osigset; + bool sigpipe_masked = false; + bool sigpipe_pending; +#endif #if !defined(ENABLE_THREAD_SAFETY) && !defined(WIN32) pqsigfunc oldsigpipehandler = NULL; #endif @@ -189,7 +193,8 @@ PQprint(FILE *fout, { usePipe = 1; #ifdef ENABLE_THREAD_SAFETY - pthread_setspecific(pq_thread_in_send, "t"); + pq_block_sigpipe(&osigset, &sigpipe_pending); + sigpipe_masked = true; #else #ifndef WIN32 oldsigpipehandler = pqsignal(SIGPIPE, SIG_IGN); @@ -311,7 +316,8 @@ PQprint(FILE *fout, pclose(fout); #endif #ifdef ENABLE_THREAD_SAFETY - pthread_setspecific(pq_thread_in_send, "f"); + if (sigpipe_masked) + pq_reset_sigpipe(&osigset, sigpipe_pending); #else #ifndef WIN32 pqsignal(SIGPIPE, oldsigpipehandler); diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c index 6b8d98001d6..79ea192f99c 100644 --- a/src/interfaces/libpq/fe-secure.c +++ b/src/interfaces/libpq/fe-secure.c @@ -11,7 +11,7 @@ * * * 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 * [ Most of these notes are wrong/obsolete, but perhaps not all ] @@ -152,12 +152,6 @@ bool pq_initssllib = true; static SSL_CTX *SSL_context = NULL; #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 */ /* ------------------------------------------------------------ */ @@ -379,9 +373,12 @@ ssize_t pqsecure_write(PGconn *conn, const void *ptr, size_t len) { ssize_t n; - + #ifdef ENABLE_THREAD_SAFETY - pthread_setspecific(pq_thread_in_send, "t"); + sigset_t osigmask; + bool sigpipe_pending; + + pq_block_sigpipe(&osigmask, &sigpipe_pending); #else #ifndef WIN32 pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN); @@ -452,9 +449,14 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len) else #endif 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 - pthread_setspecific(pq_thread_in_send, "f"); + pq_reset_sigpipe(&osigmask, sigpipe_pending); #else #ifndef WIN32 pqsignal(SIGPIPE, oldsighandler); @@ -1216,65 +1218,77 @@ PQgetssl(PGconn *conn) } #endif /* USE_SSL */ - #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 -pq_check_sigpipe_handler(void) -{ - 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) +int +pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending) { - /* - * If we have gotten a SIGPIPE outside send(), chain or exit if we are - * at the end of the chain. Synchronous signals are delivered to the - * thread that caused the signal. - */ - if (!PQinSend()) + sigset_t sigpipe_sigset; + sigset_t sigset; + int ret; + + sigemptyset(&sigpipe_sigset); + 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 */ - exit(128 + SIGPIPE); /* typical return value for SIG_DFL */ + /* Is there a pending SIGPIPE? */ + if (sigpending(&sigset) != 0) + return -1; + + if (sigismember(&sigset, SIGPIPE)) + *sigpipe_pending = true; 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() - * For use by SIGPIPE signal handlers; they should - * ignore SIGPIPE when libpq is in send(). This means - * that the backend has died unexpectedly. + * Discard any pending SIGPIPE and reset the signal mask. + * We might be discarding a blocked SIGPIPE that we didn't generate, + * but we document that you can't keep blocked SIGPIPE calls across + * libpq function calls. */ -pqbool -PQinSend(void) +int +pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending) { -#ifdef ENABLE_THREAD_SAFETY - return (pthread_getspecific(pq_thread_in_send) /* has it been set? */ && - *(char *) pthread_getspecific(pq_thread_in_send) == 't') ? true : false; -#else + int signo; + sigset_t sigset; - /* - * No threading: our code ignores SIGPIPE around send(). Therefore, we - * can't be in send() if we are checking from a SIGPIPE signal - * handler. - */ - return false; -#endif + /* Clear SIGPIPE only if none was pending */ + if (!sigpipe_pending) + { + if (sigpending(&sigset) != 0) + return -1; + + /* + * 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 diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 8d73fc8bf59..b472a1ae81c 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * 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); /* === 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 } #endif diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 343834f0a20..13b7e3588ce 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * 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 @@ #ifdef ENABLE_THREAD_SAFETY #include <pthread.h> +#include <signal.h> #endif #ifdef WIN32_CLIENT_ONLY @@ -475,15 +476,15 @@ 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 pq_check_sigpipe_handler(void); -extern pthread_key_t pq_thread_in_send; -#endif - #ifdef USE_SSL extern bool pq_initssllib; #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 * without the overhead of a function call |