Skip to content

Commit 83fb1ca

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 e8f82b4 commit 83fb1ca

File tree

5 files changed

+60
-2
lines changed

5 files changed

+60
-2
lines changed

configure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11301,7 +11301,7 @@ fi
1130111301
LIBS_including_readline="$LIBS"
1130211302
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
1130311303

11304-
for ac_func in cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat readlink setproctitle setsid shm_open sigprocmask symlink sync_file_range towlower utime utimes wcstombs wcstombs_l
11304+
for ac_func in cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat pthread_is_threaded_np readlink setproctitle setsid shm_open sigprocmask symlink sync_file_range towlower utime utimes wcstombs wcstombs_l
1130511305
do :
1130611306
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1130711307
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

configure.in

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

1283-
AC_CHECK_FUNCS([cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat readlink setproctitle setsid shm_open sigprocmask symlink sync_file_range towlower utime utimes wcstombs wcstombs_l])
1283+
AC_CHECK_FUNCS([cbrt dlopen fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll pstat pthread_is_threaded_np readlink setproctitle setsid shm_open sigprocmask symlink sync_file_range towlower utime utimes wcstombs wcstombs_l])
12841284

12851285
AC_REPLACE_FUNCS(fseeko)
12861286
case $host_os in

src/backend/postmaster/postmaster.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@
8787
#include <dns_sd.h>
8888
#endif
8989

90+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
91+
#include <pthread.h>
92+
#endif
93+
9094
#include "access/transam.h"
9195
#include "access/xlog.h"
9296
#include "bootstrap/bootstrap.h"
@@ -1203,6 +1207,24 @@ PostmasterMain(int argc, char *argv[])
12031207
*/
12041208
RemovePgTempFiles();
12051209

1210+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
1211+
1212+
/*
1213+
* On Darwin, libintl replaces setlocale() with a version that calls
1214+
* CFLocaleCopyCurrent() when its second argument is "" and every relevant
1215+
* environment variable is unset or empty. CFLocaleCopyCurrent() makes
1216+
* the process multithreaded. The postmaster calls sigprocmask() and
1217+
* calls fork() without an immediate exec(), both of which have undefined
1218+
* behavior in a multithreaded program. A multithreaded postmaster is the
1219+
* normal case on Windows, which offers neither fork() nor sigprocmask().
1220+
*/
1221+
if (pthread_is_threaded_np() != 0)
1222+
ereport(LOG,
1223+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
1224+
errmsg("postmaster became multithreaded during startup"),
1225+
errhint("Set the LC_ALL environment variable to a valid locale.")));
1226+
#endif
1227+
12061228
/*
12071229
* Remember postmaster startup time
12081230
*/
@@ -1660,6 +1682,15 @@ ServerLoop(void)
16601682
last_touch_time = now;
16611683
}
16621684

1685+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
1686+
1687+
/*
1688+
* With assertions enabled, check regularly for appearance of
1689+
* additional threads. All builds check at start and exit.
1690+
*/
1691+
Assert(pthread_is_threaded_np() == 0);
1692+
#endif
1693+
16631694
/*
16641695
* If we already sent SIGQUIT to children and they are slow to shut
16651696
* down, it's time to send them SIGKILL. This doesn't happen
@@ -4738,6 +4769,18 @@ SubPostmasterMain(int argc, char *argv[])
47384769
static void
47394770
ExitPostmaster(int status)
47404771
{
4772+
#ifdef HAVE_PTHREAD_IS_THREADED_NP
4773+
4774+
/*
4775+
* There is no known cause for a postmaster to become multithreaded after
4776+
* startup. Recheck to account for the possibility of unknown causes.
4777+
*/
4778+
if (pthread_is_threaded_np() != 0)
4779+
ereport(LOG,
4780+
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4781+
errmsg("postmaster became multithreaded")));
4782+
#endif
4783+
47414784
/* should cleanup shared memory and kill all backends */
47424785

47434786
/*

src/common/exec.c

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

557557
/* don't set LC_ALL in the backend */
558558
if (strcmp(app, PG_TEXTDOMAIN("postgres")) != 0)
559+
{
559560
setlocale(LC_ALL, "");
560561

562+
/*
563+
* One could make a case for reproducing here PostmasterMain()'s test
564+
* for whether the process is multithreaded. Unlike the postmaster,
565+
* no frontend program calls sigprocmask() or otherwise provides for
566+
* mutual exclusion between signal handlers. While frontends using
567+
* fork(), if multithreaded, are formally exposed to undefined
568+
* behavior, we have not witnessed a concrete bug. Therefore,
569+
* complaining about multithreading here may be mere pedantry.
570+
*/
571+
}
572+
561573
if (find_my_exec(argv0, my_exec_path) < 0)
562574
return;
563575

src/include/pg_config.h.in

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

375+
/* Define to 1 if you have the `pthread_is_threaded_np' function. */
376+
#undef HAVE_PTHREAD_IS_THREADED_NP
377+
375378
/* Define to 1 if you have the <pwd.h> header file. */
376379
#undef HAVE_PWD_H
377380

0 commit comments

Comments
 (0)