Re: libpq thread safety - Mailing list pgsql-hackers
| From | Manfred Spraul |
|---|---|
| Subject | Re: libpq thread safety |
| Date | |
| Msg-id | 4001822E.5000701@colorfullife.com Whole thread Raw |
| In response to | Re: libpq thread safety (Tom Lane <tgl@sss.pgh.pa.us>) |
| Responses |
Re: libpq thread safety
|
| List | pgsql-hackers |
Tom Lane wrote:
>Manfred Spraul <manfred@colorfullife.com> writes:
>
>
>>But what about kerberos: I'm a bit reluctant to add a forth mutex: what
>>if kerberos calls gethostbyname or getpwuid internally?
>>
>>
>
>Wouldn't help anyway, if some other part of the app also calls kerberos.
>
That's why I've proposed to use the system from openssl: The libpq user
must implement a lock callback, and libpq calls it around the critical
sections.
Attached is an untested prototype patch. What do you think?
--
Manfred
Index: src/interfaces/libpq/fe-connect.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.267
diff -u -r1.267 fe-connect.c
--- src/interfaces/libpq/fe-connect.c 9 Jan 2004 02:02:43 -0000 1.267
+++ src/interfaces/libpq/fe-connect.c 11 Jan 2004 16:54:06 -0000
@@ -885,12 +885,6 @@
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;
Index: src/interfaces/libpq/fe-secure.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/interfaces/libpq/fe-secure.c,v
retrieving revision 1.36
diff -u -r1.36 fe-secure.c
--- src/interfaces/libpq/fe-secure.c 9 Jan 2004 02:17:15 -0000 1.36
+++ src/interfaces/libpq/fe-secure.c 11 Jan 2004 16:54:07 -0000
@@ -146,11 +146,6 @@
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 */
/* ------------------------------------------------------------ */
@@ -212,6 +207,26 @@
/* ------------------------------------------------------------ */
/*
+ * Sigpipe handling.
+ * Dummy provided even for WIN32 to keep the API consistent
+ */
+pgsigpipehandler_t default_sigpipehandler;
+
+void default_sigpipehandler(bool enable, void **state)
+{
+#ifndef WIN32
+ if (enable) {
+ *state = (void*) pqsignal(SIGPIPE, SIG_IGN);
+ } else {
+ pqsignal(SIGPIPE, (pqsigfunc)*state);
+ }
+#endif
+}
+
+static pgsigpipehandler_t *g_sigpipehandler = default_sigpipehandler;
+
+
+/*
* Initialize global context
*/
int
@@ -356,12 +371,9 @@
{
ssize_t n;
-#ifdef ENABLE_THREAD_SAFETY
- pthread_setspecific(thread_in_send, "t");
-#else
#ifndef WIN32
- pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif
+ void *sigstate;
+ g_sigpipehandler(true, &sigstate);
#endif
#ifdef USE_SSL
@@ -420,12 +432,8 @@
#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
+ g_sigpipehandler(false, &sigstate);
#endif
return n;
@@ -1066,62 +1074,18 @@
#endif /* USE_SSL */
-
-#ifdef ENABLE_THREAD_SAFETY
/*
- * Check SIGPIPE handler and perhaps install our own.
+ * PQregisterSigpipeCallback
*/
-void
-check_sigpipe_handler(void)
+pgsigpipehandler_t *
+PQregisterSigpipeCallback(pgsigpipehandler_t *newhandler)
{
- pqsigfunc pipehandler;
+ pgsigpipehandler_t *prev;
- /*
- * 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.
- * Synchronous signals are delivered to the thread
- * that caused the signal.
- */
- 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
+ prev = g_sigpipehandler;
+ if (newhandler)
+ g_sigpipehandler = newhandler;
+ else
+ g_sigpipehandler = default_sigpipehandler;
+ return prev;
}
Index: src/interfaces/libpq/libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.102
diff -u -r1.102 libpq-fe.h
--- src/interfaces/libpq/libpq-fe.h 9 Jan 2004 02:02:43 -0000 1.102
+++ src/interfaces/libpq/libpq-fe.h 11 Jan 2004 16:54:07 -0000
@@ -453,10 +453,31 @@
/* === in fe-secure.c === */
/*
- * Indicates whether the libpq thread is in send().
- * Used to ignore SIGPIPE if thread is in send().
+ * Used to set callback that must protect libpq from SIGPIPE signals.
+ * A default implementation is provided for single threaded apps.
+ * Not required for WIN32.
*/
-pqbool PQinSend(void);
+typedef void (pgsigpipehandler_t)(bool enable, void **state);
+
+extern pgsigpipehandler_t *
+PQregisterSigpipeCallback(pgsigpipehandler_t *newhandler);
+
+/* === in thread.c === */
+
+/*
+ * Used to set callback that prevents concurrent access to
+ * non-thread safe functions that libpq needs.
+ * The default implementation uses a libpq internal mutex.
+ * Only required for multithreaded apps on platforms that
+ * do not support the thread-safe equivalents and that want
+ * to use the functions, too.
+ * List of functions:
+ * - stderror, getpwuid, gethostbyname.
+ * TODO: the mutex must be used around kerberos calls, too.
+ */
+typedef void (pgthreadlock_t)(bool acquire);
+
+extern pgthreadlock_t * PQregisterThreadLock(pgthreadlock_t *newhandler);
#ifdef __cplusplus
}
Index: src/interfaces/libpq/libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.84
diff -u -r1.84 libpq-int.h
--- src/interfaces/libpq/libpq-int.h 9 Jan 2004 02:02:43 -0000 1.84
+++ src/interfaces/libpq/libpq-int.h 11 Jan 2004 16:54:07 -0000
@@ -446,8 +446,12 @@
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;
+extern pgthreadlock_t *g_threadlock;
+#define pglock_thread() g_threadlock(true);
+#define pgunlock_thread() g_threadlock(false);
+#else
+#define pglock_thread() ((void)0)
+#define pgunlock_thread() ((void)0)
#endif
/*
Index: src/port/thread.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/port/thread.c,v
retrieving revision 1.14
diff -u -r1.14 thread.c
--- src/port/thread.c 29 Nov 2003 22:41:31 -0000 1.14
+++ src/port/thread.c 11 Jan 2004 16:54:07 -0000
@@ -65,6 +65,41 @@
* non-*_r functions.
*/
+#if defined(FRONTEND)
+#include "libpq-fe.h"
+#include "libpq-int.h"
+/*
+ * To keep the API consistent, the locking stubs are always provided, even
+ * if they are not required.
+ */
+pgthreadlock_t *g_threadlock;
+
+static pgthreadlock_t default_threadlock;
+static void
+default_threadlock(bool acquire)
+{
+#if defined(ENABLE_THREAD_SAFETY)
+ static pthread_mutex_t singlethread_lock = PTHREAD_MUTEX_INITIALIZER;
+ if (acquire)
+ pthread_mutex_lock(&singlethread_lock);
+ else
+ pthread_mutex_unlock(&singlethread_lock);
+#endif
+}
+
+pgthreadlock_t *
+PQregisterThreadLock(pgthreadlock_t *newhandler)
+{
+ pgthreadlock_t *prev;
+
+ prev = g_threadlock;
+ if (newhandler)
+ g_threadlock = newhandler;
+ else
+ g_threadlock = default_threadlock;
+ return prev;
+}
+#endif
/*
* Wrapper around strerror and strerror_r to use the former if it is
@@ -82,15 +117,14 @@
#else
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_STRERROR_R)
- static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_lock(&strerror_lock);
+ g_threadlock(true);
#endif
/* no strerror_r() available, just use strerror */
StrNCpy(strerrbuf, strerror(errnum), buflen);
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_STRERROR_R)
- pthread_mutex_unlock(&strerror_lock);
+ g_threadlock(false);
#endif
return strerrbuf;
@@ -118,8 +152,7 @@
#else
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) && !defined(HAVE_GETPWUID_R)
- static pthread_mutex_t getpwuid_lock = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_lock(&getpwuid_lock);
+ g_threadlock(true);
#endif
/* no getpwuid_r() available, just use getpwuid() */
@@ -161,7 +194,7 @@
errno = ERANGE;
}
- pthread_mutex_unlock(&getpwuid_lock);
+ g_threadlock(false);
#endif
#endif
return (*result == NULL) ? -1 : 0;
@@ -192,8 +225,7 @@
#else
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) &&
!defined(HAVE_GETHOSTBYNAME_R)
- static pthread_mutex_t gethostbyname_lock = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_lock(&gethostbyname_lock);
+ g_threadlock(true);
#endif
/* no gethostbyname_r(), just use gethostbyname() */
@@ -269,7 +301,7 @@
*herrno = h_errno;
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(NEED_REENTRANT_FUNCS) &&
!defined(HAVE_GETHOSTBYNAME_R)
- pthread_mutex_unlock(&gethostbyname_lock);
+ g_threadlock(false);
#endif
if (*result != NULL)
pgsql-hackers by date: