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:

Previous
From: Tom Lane
Date:
Subject: Re: Suggestions for analyze patch required...
Next
From: Tom Lane
Date:
Subject: Re: libpq thread safety