summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoah Misch2015-01-08 03:35:44 +0000
committerNoah Misch2015-01-08 03:46:20 +0000
commit2e4946169d2f4890606305bef8bad7883d2e5281 (patch)
tree0a83be51855227e55468901592662aa5f4018cd3
parent3580397fb142624d5602388c8149107511547573 (diff)
On Darwin, detect and report a multithreaded postmaster.
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).
-rwxr-xr-xconfigure3
-rw-r--r--configure.in2
-rw-r--r--src/backend/postmaster/postmaster.c43
-rw-r--r--src/include/pg_config.h.in3
-rw-r--r--src/port/exec.c12
5 files changed, 61 insertions, 2 deletions
diff --git a/configure b/configure
index fc4e4fa2756..5ee26cd4cfa 100755
--- a/configure
+++ b/configure
@@ -18839,7 +18839,8 @@ LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
+
+for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat pthread_is_threaded_np readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
do
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
diff --git a/configure.in b/configure.in
index 77a9a0ed89f..8dbcbccd1c2 100644
--- a/configure.in
+++ b/configure.in
@@ -1210,7 +1210,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 fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
+AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat pthread_is_threaded_np readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
AC_REPLACE_FUNCS(fseeko)
case $host_os in
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index de8c996abd5..480aab018d7 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -92,6 +92,10 @@
#include <dns_sd.h>
#endif
+#ifdef HAVE_PTHREAD_IS_THREADED_NP
+#include <pthread.h>
+#endif
+
#include "access/transam.h"
#include "access/xlog.h"
#include "bootstrap/bootstrap.h"
@@ -1081,6 +1085,24 @@ PostmasterMain(int argc, char *argv[])
}
load_ident();
+#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
*/
@@ -1506,6 +1528,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
}
}
@@ -4155,6 +4186,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 */
/*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index eb9b8cca3f9..49ef7120878 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -387,6 +387,9 @@
/* 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 <pwd.h> header file. */
#undef HAVE_PWD_H
diff --git a/src/port/exec.c b/src/port/exec.c
index 1ef2b11c034..4193bd2686a 100644
--- a/src/port/exec.c
+++ b/src/port/exec.c
@@ -570,8 +570,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;