Skip to content

Commit 8dc8310

Browse files
committed
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).
1 parent 05bb4fc commit 8dc8310

File tree

5 files changed

+61
-2
lines changed

5 files changed

+61
-2
lines changed

configure

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19157,7 +19157,8 @@ LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
1915719157

1915819158

1915919159

19160-
for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l
19160+
19161+
for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat pthread_is_threaded_np readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l
1916119162
do
1916219163
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1916319164
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5

configure.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,7 @@ PGAC_FUNC_GETTIMEOFDAY_1ARG
12401240
LIBS_including_readline="$LIBS"
12411241
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
12421242

1243-
AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l])
1243+
AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat pthread_is_threaded_np readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l])
12441244

12451245
AC_REPLACE_FUNCS(fseeko)
12461246
case $host_os in

src/backend/postmaster/postmaster.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@
9292
#include <dns_sd.h>
9393
#endif
9494

95+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
96+
#include <pthread.h>
97+
#endif
98+
9599
#include "access/transam.h"
96100
#include "access/xlog.h"
97101
#include "bootstrap/bootstrap.h"
@@ -1105,6 +1109,24 @@ PostmasterMain(int argc, char *argv[])
11051109
*/
11061110
RemovePgTempFiles();
11071111

1112+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
1113+
1114+
/*
1115+
* On Darwin, libintl replaces setlocale() with a version that calls
1116+
* CFLocaleCopyCurrent() when its second argument is "" and every relevant
1117+
* environment variable is unset or empty. CFLocaleCopyCurrent() makes
1118+
* the process multithreaded. The postmaster calls sigprocmask() and
1119+
* calls fork() without an immediate exec(), both of which have undefined
1120+
* behavior in a multithreaded program. A multithreaded postmaster is the
1121+
* normal case on Windows, which offers neither fork() nor sigprocmask().
1122+
*/
1123+
if (pthread_is_threaded_np() != 0)
1124+
ereport(LOG,
1125+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1126+
errmsg("postmaster became multithreaded during startup"),
1127+
errhint("Set the LC_ALL environment variable to a valid locale.")));
1128+
#endif
1129+
11081130
/*
11091131
* Remember postmaster startup time
11101132
*/
@@ -1535,6 +1557,15 @@ ServerLoop(void)
15351557
TouchSocketLockFile();
15361558
last_touch_time = now;
15371559
}
1560+
1561+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
1562+
1563+
/*
1564+
* With assertions enabled, check regularly for appearance of
1565+
* additional threads. All builds check at start and exit.
1566+
*/
1567+
Assert(pthread_is_threaded_np() == 0);
1568+
#endif
15381569
}
15391570
}
15401571

@@ -4209,6 +4240,18 @@ SubPostmasterMain(int argc, char *argv[])
42094240
static void
42104241
ExitPostmaster(int status)
42114242
{
4243+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
4244+
4245+
/*
4246+
* There is no known cause for a postmaster to become multithreaded after
4247+
* startup. Recheck to account for the possibility of unknown causes.
4248+
*/
4249+
if (pthread_is_threaded_np() != 0)
4250+
ereport(LOG,
4251+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4252+
errmsg("postmaster became multithreaded")));
4253+
#endif
4254+
42124255
/* should cleanup shared memory and kill all backends */
42134256

42144257
/*

src/include/pg_config.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@
393393
/* Define if you have POSIX threads libraries and header files. */
394394
#undef HAVE_PTHREAD
395395

396+
/* Define to 1 if you have the `pthread_is_threaded_np' function. */
397+
#undef HAVE_PTHREAD_IS_THREADED_NP
398+
396399
/* Define to 1 if you have the <pwd.h> header file. */
397400
#undef HAVE_PWD_H
398401

src/port/exec.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,8 +570,20 @@ set_pglocale_pgservice(const char *argv0, const char *app)
570570

571571
/* don't set LC_ALL in the backend */
572572
if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
573+
{
573574
setlocale(LC_ALL, "");
574575

576+
/*
577+
* One could make a case for reproducing here PostmasterMain()'s test
578+
* for whether the process is multithreaded. Unlike the postmaster,
579+
* no frontend program calls sigprocmask() or otherwise provides for
580+
* mutual exclusion between signal handlers. While frontends using
581+
* fork(), if multithreaded, are formally exposed to undefined
582+
* behavior, we have not witnessed a concrete bug. Therefore,
583+
* complaining about multithreading here may be mere pedantry.
584+
*/
585+
}
586+
575587
if (find_my_exec(argv0, my_exec_path) < 0)
576588
return;
577589

0 commit comments

Comments
 (0)