On Darwin, detect and report a multithreaded postmaster.
authorNoah Misch
Thu, 8 Jan 2015 03:35:44 +0000 (22:35 -0500)
committerNoah Misch
Thu, 8 Jan 2015 03:41:49 +0000 (22:41 -0500)
Darwin --enable-nls builds use a substitute setlocale() that may start a
thread.  Buildfarm member orangutan experienced BackendList corruption
on account of different postmaster threads executing signal handlers
simultaneously.  Furthermore, a multithreaded postmaster risks undefined
behavior from sigprocmask() and fork().  Emit LOG messages about the
problem and its workaround.  Back-patch to 9.0 (all supported versions).

configure
configure.in
src/backend/postmaster/postmaster.c
src/include/pg_config.h.in
src/port/exec.c

index 01e3776ce63cc492952a6310af17e3fb42c17fef..1dd02667ab3f86e46831df821000ca89426b4aa9 100755 (executable)
--- a/configure
+++ b/configure
@@ -19302,7 +19302,8 @@ LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
 
 
 
-for ac_func in cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat readlink setproctitle setsid sigprocmask symlink towlower utime utimes waitpid wcstombs wcstombs_l
+
+for ac_func in cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat pthread_is_threaded_np readlink setproctitle setsid sigprocmask symlink towlower utime utimes waitpid wcstombs wcstombs_l
 do
 as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 { $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
index 2579e9bda45e98cc4ee579ed65773001ed07acd2..2644d3a806912cfbbd9b3d99912c15bfca240929 100644 (file)
@@ -1241,7 +1241,7 @@ PGAC_FUNC_GETTIMEOFDAY_1ARG
 LIBS_including_readline="$LIBS"
 LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
 
-AC_CHECK_FUNCS([cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat readlink setproctitle setsid sigprocmask symlink towlower utime utimes waitpid wcstombs wcstombs_l])
+AC_CHECK_FUNCS([cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat pthread_is_threaded_np readlink setproctitle setsid sigprocmask symlink towlower utime utimes waitpid wcstombs wcstombs_l])
 
 AC_REPLACE_FUNCS(fseeko)
 case $host_os in
index 693880a87b04f783789322dffb49198dc1207f7b..4981152e37d46ee539bd93be23694d6c608eb23b 100644 (file)
 #include 
 #endif
 
+#ifdef HAVE_PTHREAD_IS_THREADED_NP
+#include 
+#endif
+
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "bootstrap/bootstrap.h"
@@ -1105,6 +1109,24 @@ PostmasterMain(int argc, char *argv[])
     */
    RemovePgTempFiles();
 
+#ifdef HAVE_PTHREAD_IS_THREADED_NP
+
+   /*
+    * On Darwin, libintl replaces setlocale() with a version that calls
+    * CFLocaleCopyCurrent() when its second argument is "" and every relevant
+    * environment variable is unset or empty.  CFLocaleCopyCurrent() makes
+    * the process multithreaded.  The postmaster calls sigprocmask() and
+    * calls fork() without an immediate exec(), both of which have undefined
+    * behavior in a multithreaded program.  A multithreaded postmaster is the
+    * normal case on Windows, which offers neither fork() nor sigprocmask().
+    */
+   if (pthread_is_threaded_np() != 0)
+       ereport(LOG,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("postmaster became multithreaded during startup"),
+        errhint("Set the LC_ALL environment variable to a valid locale.")));
+#endif
+
    /*
     * Remember postmaster startup time
     */
@@ -1441,6 +1463,15 @@ ServerLoop(void)
            TouchSocketLockFile();
            last_touch_time = now;
        }
+
+#ifdef HAVE_PTHREAD_IS_THREADED_NP
+
+       /*
+        * With assertions enabled, check regularly for appearance of
+        * additional threads.  All builds check at start and exit.
+        */
+       Assert(pthread_is_threaded_np() == 0);
+#endif
    }
 }
 
@@ -4211,6 +4242,18 @@ SubPostmasterMain(int argc, char *argv[])
 static void
 ExitPostmaster(int status)
 {
+#ifdef HAVE_PTHREAD_IS_THREADED_NP
+
+   /*
+    * There is no known cause for a postmaster to become multithreaded after
+    * startup.  Recheck to account for the possibility of unknown causes.
+    */
+   if (pthread_is_threaded_np() != 0)
+       ereport(LOG,
+               (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                errmsg("postmaster became multithreaded")));
+#endif
+
    /* should cleanup shared memory and kill all backends */
 
    /*
index d42184aac1f8353b713bcd1da89bf2fa20782756..f43caed66c03aba8ff3d4b4627b54282f6a77a39 100644 (file)
 /* Define if you have POSIX threads libraries and header files. */
 #undef HAVE_PTHREAD
 
+/* Define to 1 if you have the `pthread_is_threaded_np' function. */
+#undef HAVE_PTHREAD_IS_THREADED_NP
+
 /* Define to 1 if you have the  header file. */
 #undef HAVE_PWD_H
 
index 0726dbe8506ff4b6fd43d171225446db5efb5ae8..cf6c64ebc7551ccc0612ccaa1a01bb8f29d7c1a3 100644 (file)
@@ -571,8 +571,20 @@ set_pglocale_pgservice(const char *argv0, const char *app)
 
    /* don't set LC_ALL in the backend */
    if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
+   {
        setlocale(LC_ALL, "");
 
+       /*
+        * One could make a case for reproducing here PostmasterMain()'s test
+        * for whether the process is multithreaded.  Unlike the postmaster,
+        * no frontend program calls sigprocmask() or otherwise provides for
+        * mutual exclusion between signal handlers.  While frontends using
+        * fork(), if multithreaded, are formally exposed to undefined
+        * behavior, we have not witnessed a concrete bug.  Therefore,
+        * complaining about multithreading here may be mere pedantry.
+        */
+   }
+
    if (find_my_exec(argv0, my_exec_path) < 0)
        return;