Add the __libc_single_threaded variable
diff mbox series

Message ID 87blwpyo45.fsf@oldenburg2.str.redhat.com
State New
Headers show
Series
  • Add the __libc_single_threaded variable
Related show

Commit Message

Florian Weimer Aug. 16, 2019, 6:14 p.m. UTC
This version is based on the earlier feedback that a simple global
variable should be used instead

  <https://sourceware.org/ml/libc-alpha/2019-02/msg00073.html>

and the benchmarking results suggesting that this is feasible:

  <https://sourceware.org/ml/libc-alpha/2019-06/msg00954.html>

The implementation is somewhat complicated by copy relocations, which
are not applied to the inner namespace.

This is probably the simplest possible implementation.  It does not
attempt to reset the variable if there is only one thread left.

2019-08-16  Florian Weimer  <fweimer@redhat.com>

	Add the __libc_single_threaded variable.
	* elf/Makefile (tests-static-normal): Add
	tst-single_threaded-static, tst-single_threaded-pthread-static.
	[$(build-shared)] (tests-static): Add tst-tls9-static,
	tst-single_threaded-static-dlopen.
	[$(build-shared)] (static-dlopen-environment): New variable.
	[$(build-shared)] (tst-tls9-static-ENV): Use it.
	[$(build-shared)] (tst-single_threaded-static-dlopen-ENV): Set.
	[$(build-shared)] (module-names): Add st-single_threaded-mod1,
	tst-single_threaded-mod2, tst-single_threaded-mod3,
	tst-single_threaded-mod4.
	(tst-single_threaded): Link with tst-single_threaded-mod1.so and
	-ldl.
	(tst-single_threaded.out): Depend on tst-single_threaded-mod2.so,
	tst-single_threaded-mod3.so.
	(tst-single_threaded-static-dlopen): Link with
	tst-single_threaded-mod1.o, -ldl.
	(tst-single_threaded-static-dlopen.out): Depend on
	tst-single_threaded-mod2.so.
	(tst-single_threaded-pthread): Link with
	tst-single_threaded-mod1.so, -lpthread.
	(tst-single_threaded-pthread.out): Depend on tst-single_threaded-mod2.so,
	tst-single_threaded-mod3.so, tst-single_threaded-mod4.so.
	(tst-single_threaded-pthread-static): Link with -lpthread.
	* elf/Versions (ld GLIBC_2.31): Export __libc_single_threaded.
	(ld GLIBC_PRIVATE): Export _dl_single_threaded_update.
	* elf/dl-open.c (_dl_open): Call
	__libc_single_threaded_dlopen_called.
	* elf/rtld.c (__libc_single_threaded): Define variable.
	(__libc_single_threaded_local): Declare as alias.
	(_dl_single_threaded_update): New function.
	(dl_main): Initialize it.
	* elf/tst-single_threaded-mod1.c: New file.
	* elf/tst-single_threaded-mod2.c: Likewise.
	* elf/tst-single_threaded-mod3.c: Likewise.
	* elf/tst-single_threaded-mod4.c: Likewise.
	* elf/tst-single_threaded-pthread-static.c: Likewise.
	* elf/tst-single_threaded-pthread.c: Likewise.
	* elf/tst-single_threaded-static-dlopen.c: Likewise.
	* elf/tst-single_threaded-static.c: Likewise.
	* elf/tst-single_threaded.c: Likewise.
	* htl/pt-create.c (__pthread_create_internal): Call
	__libc_single_threaded_create_thread.
	* include/sys/single_threaded.h: New file.
	* manual/threads.texi (Threads): Reference Single-Threaded node.
	(Single-Threaded): New.
	* misc/Makefile (headers): Add sys/single_threaded.h.
	(routines): Add single_threaded.
	* misc/single_threaded.c: New file.
	* misc/sys/single_threaded.h: Likewise.
	* nptl/pthread_create.c (__pthread_create_2_1): Call
	__libc_single_threaded_create_thread.
	* sysdeps/generic/ldsodefs.h (__libc_single_threaded)
	(__libc_single_threaded_local, _dl_single_threaded_update):
	Declare.
	* sysdeps/mach/hurd/fork.c (__fork): Call
	__libc_single_threaded_subprocess_after_fork.
	* sysdeps/mach/hurd/i386/ld.abilist (GLIBC_2.31): Add
	__libc_single_threaded.
	* sysdeps/nptl/fork.c (__libc_fork): Call
	__libc_single_threaded_subprocess_after_fork.
	* sysdeps/unix/sysv/linux/aarch64/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/alpha/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/arm/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/csky/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/hppa/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/i386/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/ia64/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdepsq/unix/sysv/linux/m68k/m680x0/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/microblaze/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n32/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/nios2/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/ld.abilist
	(GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/be/ld.abilist
	(GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/le/ld.abilist
	(GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/riscv/rv64/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/sh/ld.abilist (GLIBC_2.31): Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/ld.abilist (GLIBC_2.31):
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/ld.abilist (GLIBC_2.31):
	Likewise.

Patch
diff mbox series

diff --git a/NEWS b/NEWS
index 045720b3fb..d3055b8145 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,11 @@  Major new features:
   liable to change.  Features from C2X are also enabled by _GNU_SOURCE, or
   by compiling with "gcc -std=gnu2x".
 
+* The GNU C Library now provides the header file <sys/single_threaded.h>
+  which provides the variable __libc_single_threaded.  Applications are
+  encouraged to use this variable for single-thread optimizations, instead
+  of weak references to symbols historically defined in libpthread.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The totalorder and totalordermag functions, and the corresponding
@@ -25,6 +30,11 @@  Deprecated and removed features, and other changes affecting compatibility:
   Request 25 to TS 18661-1, as applied for C2X.  Existing binaries that pass
   floating-point arguments directly will continue to work.
 
+* Using the undefined status of weak references to symbols defined
+  libpthread to determine whether a process can be multi-threaded has been
+  deprecated.  It is expected that all libpthread symbols will move into
+  libc in a future version of the GNU C Library.
+
 Changes to build and runtime requirements:
 
   [Add changes to build and runtime requirements here]
diff --git a/elf/Makefile b/elf/Makefile
index d470e41402..e643e8a236 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -148,7 +148,9 @@  endif
 tests-static-normal := tst-leaks1-static tst-array1-static tst-array5-static \
 	       tst-dl-iter-static \
 	       tst-tlsalign-static tst-tlsalign-extern-static \
-	       tst-linkall-static tst-env-setuid tst-env-setuid-tunables
+	       tst-linkall-static tst-env-setuid tst-env-setuid-tunables \
+	       tst-single_threaded-static tst-single_threaded-pthread-static
+
 tests-static-internal := tst-tls1-static tst-tls2-static \
 	       tst-ptrguard1-static tst-stackguard1-static \
 	       tst-tls1-static-non-pie tst-libc_dlvsym-static
@@ -166,9 +168,11 @@  tests-internal := tst-tls1 tst-tls2 $(tests-static-internal)
 tests-static := $(tests-static-normal) $(tests-static-internal)
 
 ifeq (yes,$(build-shared))
-tests-static += tst-tls9-static
-tst-tls9-static-ENV = \
-       LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
+tests-static += tst-tls9-static tst-single_threaded-static-dlopen
+static-dlopen-environment = \
+  LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn
+tst-tls9-static-ENV = $(static-dlopen-environment)
+tst-single_threaded-static-dlopen-ENV = $(static-dlopen-environment)
 
 tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 constload1 order noload filter \
@@ -192,7 +196,8 @@  tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
 	 tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
 	 tst-unwind-ctor tst-unwind-main tst-audit13 \
-	 tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout
+	 tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-aout \
+	 tst-single_threaded tst-single_threaded-pthread
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -279,7 +284,9 @@  modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
 		tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \
 		tst-audit13mod1 tst-sonamemove-linkmod1 \
-		tst-sonamemove-runmod1 tst-sonamemove-runmod2
+		tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
+		tst-single_threaded-mod1 tst-single_threaded-mod2 \
+		tst-single_threaded-mod3 tst-single_threaded-mod4
 # Most modules build with _ISOMAC defined, but those filtered out
 # depend on internal headers.
 modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
@@ -1553,3 +1560,17 @@  $(objpfx)tst-big-note-lib.so: $(objpfx)tst-big-note-lib.o
 $(objpfx)tst-unwind-ctor: $(objpfx)tst-unwind-ctor-lib.so
 
 CFLAGS-tst-unwind-main.c += -funwind-tables -DUSE_PTHREADS=0
+
+$(objpfx)tst-single_threaded: $(objpfx)tst-single_threaded-mod1.so $(libdl)
+$(objpfx)tst-single_threaded.out: \
+  $(objpfx)tst-single_threaded-mod2.so $(objpfx)tst-single_threaded-mod3.so
+$(objpfx)tst-single_threaded-static-dlopen: \
+  $(objpfx)tst-single_threaded-mod1.o $(common-objpfx)dlfcn/libdl.a
+$(objpfx)tst-single_threaded-static-dlopen.out: \
+  $(objpfx)tst-single_threaded-mod2.so
+$(objpfx)tst-single_threaded-pthread: \
+  $(objpfx)tst-single_threaded-mod1.so $(libdl) $(shared-thread-library)
+$(objpfx)tst-single_threaded-pthread.out: \
+  $(objpfx)tst-single_threaded-mod2.so $(objpfx)tst-single_threaded-mod3.so \
+  $(objpfx)tst-single_threaded-mod4.so
+$(objpfx)tst-single_threaded-pthread-static: $(static-thread-library)
diff --git a/elf/Versions b/elf/Versions
index 3b09901f6c..56b47d13cd 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -54,6 +54,9 @@  ld {
     # stack canary
     __stack_chk_guard;
   }
+  GLIBC_2.31 {
+    __libc_single_threaded;
+  }
   GLIBC_PRIVATE {
     # Those are in the dynamic linker, but used by libc.so.
     __libc_enable_secure;
@@ -62,6 +65,7 @@  ld {
     _dl_deallocate_tls; _dl_make_stack_executable;
     _dl_rtld_di_serinfo; _dl_starting_up;
     _rtld_global; _rtld_global_ro;
+    _dl_single_threaded_update;
 
     # Only here for gdb while a better method is developed.
     _dl_debug_state;
diff --git a/elf/dl-open.c b/elf/dl-open.c
index e18ee398cb..6699149723 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -33,6 +33,7 @@ 
 #include <stap-probe.h>
 #include <atomic.h>
 #include <libc-internal.h>
+#include <sys/single_threaded.h>
 
 #include <dl-dst.h>
 #include <dl-prop.h>
@@ -537,6 +538,8 @@  _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
     /* One of the flags must be set.  */
     _dl_signal_error (EINVAL, file, NULL, N_("invalid mode for dlopen()"));
 
+  __libc_single_threaded_dlopen_called ();
+
   /* Make sure we are alone.  */
   __rtld_lock_lock_recursive (GL(dl_load_lock));
 
diff --git a/elf/rtld.c b/elf/rtld.c
index c9490ff694..4d254a177e 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -43,6 +43,7 @@ 
 #include <stap-probe.h>
 #include <stackinfo.h>
 #include <not-cancel.h>
+#include <sys/single_threaded.h>
 
 #include <assert.h>
 
@@ -338,6 +339,37 @@  extern struct rtld_global_ro _rtld_local_ro
     __attribute__ ((alias ("_rtld_global_ro"), visibility ("hidden")));
 
 
+/* This variable is initialized to zero and set to 1 in dl_main.  The
+   reason for that is that dl_main is not run after static dlopen,
+   which means that the inner libc conservatively uses a value zero
+   for __libc_single_threaded.
+
+   A second copy of the variable may also exist in inner namespaces if
+   the main program triggered a copy relocation.  Both can be updated
+   via _dl_single_threaded_update.  The definition of the variable and
+   its alias follows the definition of _rtld_global and
+   _rtld_local.  */
+char __libc_single_threaded = 0;
+extern char __libc_single_threaded_local
+  __attribute__ ((alias ("__libc_single_threaded"), visibility ("hidden")));
+
+/* This function is necessary to update both copies of the variable at
+   the same time, in case a copy relocation in the main program
+   created a copy and inner namespaces exist which see the original
+   variable.  */
+void
+_dl_single_threaded_update (char value)
+{
+  /* If the dynamic linker is not active, the statically linked main
+     program can create threads in an unnoticed fashion.  The flag
+     therefore must not be reset to 1.  */
+  if (value == 0 || rtld_active ())
+    {
+      __libc_single_threaded = value;
+      __libc_single_threaded_local = value;
+    }
+}
+
 static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
 		     ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv);
 
@@ -1117,6 +1149,10 @@  dl_main (const ElfW(Phdr) *phdr,
   _dl_starting_up = 1;
 #endif
 
+  /* There is just one thread running in the process at this point.
+     Perform this early, so that it affects both copies.  */
+  __libc_single_threaded = 1;
+
   if (*user_entry == (ElfW(Addr)) ENTRY_POINT)
     {
       /* Ho ho.  We are not the program interpreter!  We are the program
diff --git a/elf/tst-single_threaded-mod1.c b/elf/tst-single_threaded-mod1.c
new file mode 100644
index 0000000000..9fe94b2526
--- /dev/null
+++ b/elf/tst-single_threaded-mod1.c
@@ -0,0 +1,25 @@ 
+/* Test support for single-thread optimizations.  Shared object 1.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/single_threaded.h>
+
+_Bool
+single_threaded_1 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-mod2.c b/elf/tst-single_threaded-mod2.c
new file mode 100644
index 0000000000..a5166c9ebc
--- /dev/null
+++ b/elf/tst-single_threaded-mod2.c
@@ -0,0 +1,25 @@ 
+/* Test support for single-thread optimizations.  Shared object 2.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/single_threaded.h>
+
+_Bool
+single_threaded_2 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-mod3.c b/elf/tst-single_threaded-mod3.c
new file mode 100644
index 0000000000..53df13e3a7
--- /dev/null
+++ b/elf/tst-single_threaded-mod3.c
@@ -0,0 +1,25 @@ 
+/* Test support for single-thread optimizations.  Shared object 3.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/single_threaded.h>
+
+_Bool
+single_threaded_3 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-mod4.c b/elf/tst-single_threaded-mod4.c
new file mode 100644
index 0000000000..3bf5e555a4
--- /dev/null
+++ b/elf/tst-single_threaded-mod4.c
@@ -0,0 +1,25 @@ 
+/* Test support for single-thread optimizations.  Shared object 4.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/single_threaded.h>
+
+_Bool
+single_threaded_4 (void)
+{
+  return __libc_single_threaded;
+}
diff --git a/elf/tst-single_threaded-pthread-static.c b/elf/tst-single_threaded-pthread-static.c
new file mode 100644
index 0000000000..780564c40c
--- /dev/null
+++ b/elf/tst-single_threaded-pthread-static.c
@@ -0,0 +1,86 @@ 
+/* Test support for single-thread optimizations.  With threads, static version.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* This test is a stripped-down version of
+   tst-single_threaded-pthread.c, without any loading of dynamic
+   objects.  */
+
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xthread.h>
+#include <sys/single_threaded.h>
+
+/* First barrier synchronizes main thread, thread 1, thread 2.  */
+static pthread_barrier_t barrier1;
+
+/* Second barrier synchronizes main thread, thread 2.  */
+static pthread_barrier_t barrier2;
+
+static void *
+threadfunc (void *closure)
+{
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* Wait for the main thread and the other thread.  */
+  xpthread_barrier_wait (&barrier1);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* Second thread waits on second barrier, too.  */
+  if (closure != NULL)
+    xpthread_barrier_wait (&barrier2);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  return NULL;
+}
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+
+  /* Two threads plus main thread.  */
+  xpthread_barrier_init (&barrier1, NULL, 3);
+
+  /* Main thread and second thread.  */
+  xpthread_barrier_init (&barrier2, NULL, 2);
+
+  pthread_t thr1 = xpthread_create (NULL, threadfunc, NULL);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  pthread_t thr2 = xpthread_create (NULL, threadfunc, &thr2);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  xpthread_barrier_wait (&barrier1);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* Join first thread.  This should not bring us back into
+     single-threaded mode.  */
+  xpthread_join (thr1);
+  TEST_VERIFY (!__libc_single_threaded);
+
+  /* We may be back in single-threaded mode after joining both
+     threads, but this is not guaranteed.  */
+  xpthread_barrier_wait (&barrier2);
+  xpthread_join (thr2);
+  printf ("info: __libc_single_threaded after joining all threads: %d\n",
+          __libc_single_threaded);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded-pthread.c b/elf/tst-single_threaded-pthread.c
new file mode 100644
index 0000000000..d7548ac653
--- /dev/null
+++ b/elf/tst-single_threaded-pthread.c
@@ -0,0 +1,168 @@ 
+/* Test support for single-thread optimizations.  With threads.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xdlfcn.h>
+#include <support/xthread.h>
+#include <sys/single_threaded.h>
+
+/* First barrier synchronizes main thread, thread 1, thread 2.  */
+static pthread_barrier_t barrier1;
+
+/* Second barrier synchronizes main thread, thread 2.  */
+static pthread_barrier_t barrier2;
+
+/* Defined in tst-single-threaded-mod1.so.  */
+_Bool single_threaded_1 (void);
+
+/* Initialized via dlsym.  */
+static _Bool (*single_threaded_2) (void);
+static _Bool (*single_threaded_3) (void);
+static _Bool (*single_threaded_4) (void);
+
+static void *
+threadfunc (void *closure)
+{
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+
+  /* Wait until the main thread loads more functions.  */
+  xpthread_barrier_wait (&barrier1);
+
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+
+  /* Second thread waits on second barrier, too.  */
+  if (closure != NULL)
+    xpthread_barrier_wait (&barrier2);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+
+  return NULL;
+}
+
+/* A subprocess is always single-threaded at first.  */
+static void
+subprocess (void *closure)
+{
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  if (single_threaded_2 != NULL)
+    TEST_VERIFY (single_threaded_2 ());
+  if (single_threaded_3 != NULL)
+    TEST_VERIFY (single_threaded_3 ());
+  if (single_threaded_4 != NULL)
+    TEST_VERIFY (single_threaded_4 ());
+}
+
+static int
+do_test (void)
+{
+  printf ("info: main __libc_single_threaded address: %p\n",
+          &__libc_single_threaded);
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  void *handle_mod2 = xdlopen ("tst-single_threaded-mod2.so", RTLD_LAZY);
+  single_threaded_2 = xdlsym (handle_mod2, "single_threaded_2");
+  TEST_VERIFY (single_threaded_2 ());
+
+  /* Two threads plus main thread.  */
+  xpthread_barrier_init (&barrier1, NULL, 3);
+
+  /* Main thread and second thread.  */
+  xpthread_barrier_init (&barrier2, NULL, 2);
+
+  pthread_t thr1 = xpthread_create (NULL, threadfunc, NULL);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  pthread_t thr2 = xpthread_create (NULL, threadfunc, &thr2);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* Delayed library load, while already multi-threaded.  */
+  void *handle_mod3 = xdlopen ("tst-single_threaded-mod3.so", RTLD_LAZY);
+  single_threaded_3 = xdlsym (handle_mod3, "single_threaded_3");
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* Same with dlmopen.  */
+  void *handle_mod4 = dlmopen (LM_ID_NEWLM, "tst-single_threaded-mod4.so",
+                               RTLD_LAZY);
+  single_threaded_4 = xdlsym (handle_mod4, "single_threaded_4");
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* Run the newly loaded functions from the other threads as
+     well.  */
+  xpthread_barrier_wait (&barrier1);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* Join first thread.  This should not bring us back into
+     single-threaded mode.  */
+  xpthread_join (thr1);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+  TEST_VERIFY (!single_threaded_3 ());
+  TEST_VERIFY (!single_threaded_4 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* We may be back in single-threaded mode after joining both
+     threads, but this is not guaranteed.  */
+  xpthread_barrier_wait (&barrier2);
+  xpthread_join (thr2);
+  printf ("info: __libc_single_threaded after joining all threads: %d\n",
+          __libc_single_threaded);
+
+  xdlclose (handle_mod4);
+  xdlclose (handle_mod3);
+  xdlclose (handle_mod2);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded-static-dlopen.c b/elf/tst-single_threaded-static-dlopen.c
new file mode 100644
index 0000000000..7e7847e7ab
--- /dev/null
+++ b/elf/tst-single_threaded-static-dlopen.c
@@ -0,0 +1,55 @@ 
+/* Test support for single-thread optimizations.  No threads, static dlopen.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* In a static dlopen scenario, the single-threaded optimization is
+   not possible because their is no globally shared dynamic linker
+   across all namespaces.  */
+
+#include <stddef.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <sys/single_threaded.h>
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+
+  /* Defined in tst-single-threaded-mod1.o.  */
+  extern _Bool single_threaded_1 (void);
+  TEST_VERIFY (single_threaded_1 ());
+
+  /* Even after a failed dlopen, assume multi-threaded mode.  */
+  TEST_VERIFY (dlopen ("tst-single_threaded-does-not-exist.so", RTLD_LAZY)
+               == NULL);
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+
+  void *handle_mod2 = xdlopen ("tst-single_threaded-mod2.so", RTLD_LAZY);
+  _Bool (*single_threaded_2) (void)
+    = xdlsym (handle_mod2, "single_threaded_2");
+  TEST_VERIFY (!__libc_single_threaded);
+  TEST_VERIFY (!single_threaded_1 ());
+  TEST_VERIFY (!single_threaded_2 ());
+
+  xdlclose (handle_mod2);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded-static.c b/elf/tst-single_threaded-static.c
new file mode 100644
index 0000000000..29d7ab2731
--- /dev/null
+++ b/elf/tst-single_threaded-static.c
@@ -0,0 +1,29 @@ 
+/* Test support for single-thread optimizations.  Static, no threads.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <sys/single_threaded.h>
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-single_threaded.c b/elf/tst-single_threaded.c
new file mode 100644
index 0000000000..8911c64129
--- /dev/null
+++ b/elf/tst-single_threaded.c
@@ -0,0 +1,71 @@ 
+/* Test support for single-thread optimizations.  No threads.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/namespace.h>
+#include <support/xdlfcn.h>
+#include <sys/single_threaded.h>
+
+/* Defined in tst-single-threaded-mod1.so.  */
+extern _Bool single_threaded_1 (void);
+
+/* Initialized via dlsym.  */
+_Bool (*single_threaded_2) (void);
+_Bool (*single_threaded_3) (void);
+
+static void
+subprocess (void *closure)
+{
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  if (single_threaded_2 != NULL)
+    TEST_VERIFY (single_threaded_2 ());
+  if (single_threaded_3 != NULL)
+    TEST_VERIFY (single_threaded_3 ());
+}
+
+static int
+do_test (void)
+{
+  TEST_VERIFY (__libc_single_threaded);
+  TEST_VERIFY (single_threaded_1 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  void *handle_mod2 = xdlopen ("tst-single_threaded-mod2.so", RTLD_LAZY);
+  single_threaded_2 = xdlsym (handle_mod2, "single_threaded_2");
+  TEST_VERIFY (single_threaded_2 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  /* The current implementation currently treats the inner namespace
+     as multi-threaded if there is a copy relocation in the main
+     program.  */
+  void *handle_mod3 = dlmopen (LM_ID_NEWLM, "tst-single_threaded-mod3.so",
+                               RTLD_LAZY);
+  single_threaded_3 = xdlsym (handle_mod3, "single_threaded_3");
+  TEST_VERIFY (single_threaded_3 ());
+  support_isolate_in_subprocess (subprocess, NULL);
+
+  xdlclose (handle_mod3);
+  xdlclose (handle_mod2);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/htl/pt-create.c b/htl/pt-create.c
index 4f732001f8..be20403756 100644
--- a/htl/pt-create.c
+++ b/htl/pt-create.c
@@ -24,6 +24,7 @@ 
 
 #include <atomic.h>
 #include <hurd/resource.h>
+#include <sys/single_threaded.h>
 
 #include <pt-internal.h>
 #include <pthreadP.h>
@@ -94,6 +95,8 @@  __pthread_create_internal (struct __pthread **thread,
   sigset_t sigset;
   size_t stacksize;
 
+  __libc_single_threaded_create_thread ();
+
   /* Allocate a new thread structure.  */
   err = __pthread_alloc (&pthread);
   if (err)
diff --git a/include/sys/single_threaded.h b/include/sys/single_threaded.h
new file mode 100644
index 0000000000..b75f41b0b9
--- /dev/null
+++ b/include/sys/single_threaded.h
@@ -0,0 +1,74 @@ 
+/* Support for single-thread optimizations; wrapper header.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _SYS_SINGLE_THREADED_H
+
+#ifdef _ISOMAC
+# include <misc/sys/single_threaded.h>
+#else
+
+# ifdef SHARED
+/* Inside a dynamically linked glibc, the definition of the variable
+   comes from this header.  */
+#  define _SYS_SINGLE_THREADED_H
+#  include <ldsodefs.h>
+# else
+/* Statically linked programs use a simple variable instead.  */
+#  include <misc/sys/single_threaded.h>
+# endif
+
+/* Called at the beginning of the dlopen/dlmopen.  */
+static inline void
+__libc_single_threaded_dlopen_called (void)
+{
+# ifndef SHARED
+  /* If dlopen/dlmopen is called from a statically linked process,
+     conservatively assume that the inner libc can create new threads.
+     This only applies to static linking; for dynamic linkining (even
+     with dlmopen), the variable is shared via the dynamic linker and
+     is therefore accurate.  */
+  __libc_single_threaded = 0;
+# endif
+}
+
+/* Called after fork, in the subprocess.  */
+static inline void
+__libc_single_threaded_subprocess_after_fork (void)
+{
+# ifdef SHARED
+  /* Call into the dynamic loader to perform all relevant updates
+     (which may be none in the case of static dlopen).  In the
+     statically linked case, do not reset the variable, in case dlopen
+     has been called before.  */
+  _dl_single_threaded_update (1);
+# endif
+}
+
+/* Called before a new thread is created.  */
+static inline void
+__libc_single_threaded_create_thread (void)
+{
+# ifdef SHARED
+  _dl_single_threaded_update (0);
+# else
+  __libc_single_threaded = 0;
+# endif
+}
+
+#endif /* _ISOMAC */
+#endif /* _SYS_SINGLE_THREADED_H */
diff --git a/manual/threads.texi b/manual/threads.texi
index 0e5e84ab0a..715cc55acb 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -11,6 +11,7 @@  POSIX threads.
 @menu
 * ISO C Threads::	Threads based on the ISO C specification.
 * POSIX Threads::	Threads based on the POSIX specification.
+* Single-Threaded::     Detecting single-threaded execution.
 @end menu
 
 
@@ -727,6 +728,83 @@  rather than @code{CLOCK_REALTIME}.  Currently, @var{clockid} must be either
 returned.
 @end deftypefun
 
+@node Single-Threaded
+@section Detecting Single-Threaded Execution
+
+Multi-threaded programs require synchronization among threads.  This
+synchronization can be costly even if there is just a single thread
+and no data is shared between multiple processors.  For this reason,
+@theglibc{} offers an interface to detect whether the process is in
+single-threaded mode.  Applications can use this information to avoid
+synchronization, for example by using regular instructions to load and
+store memory instead of atomic instructions, or using relaxed memory
+ordering instead of stronger memory ordering.
+
+@deftypevar char __libc_single_threaded
+@standards{GNU, sys/single_threaded.h}
+This variable is non-zero if the current process is definitely
+single-threaded.  If it is zero, the process can be multi-threaded,
+or @theglibc{} cannot determine at this point of the program execution
+whether the process is single-threaded or not.
+
+Applications must never write to this variable.
+
+Applications should perform the same actions whether or not
+@code{__libc_single_threaded} is true, but only switch to a weaker
+memory ordering.  As a result, a process that becomes multi-threaded
+afterwards is already in the correct state.  For example, in order to
+increment a reference counter, the following code can be used:
+
+@smallexample
+if (__libc_single_threaded)
+  atomic_fetch_add (&reference_counter, 1, memory_order_relaxed);
+else
+  atomic_fetch_add (&reference_counter, 1, memory_order_acq_rel);
+@end smallexample
+
+This still requires some form of synchronization on the
+single-threaded branch, so it can be beneficial not to declare the
+reference counter as @code{_Atomic}, and use the GCC @code{__atomic}
+built-ins:
+
+@smallexample
+if (__libc_single_threaded)
+  ++refeference_counter;
+else
+  __atomic_fetch_add (&reference_counter, 1, __ATOMIC_ACQ_REL);
+@end smallexample
+
+Several functions in @theglibc{} can change the value of the
+@code{__libc_single_threaded} variable.  Creating new threads using
+the @code{pthread_create} or @code{thrd_create} function sets the
+variable to false.  Less obvious is that the @code{dlopen} function
+may also cause threads to be created if any of the loaded objects
+creates a thread from an ELF constructor.  Therefore, applications
+need to make a copy of the value of @code{__libc_single_threaded} if
+after such a function call, behavior must match the value as it was
+before the call, like this:
+
+@smallexample
+bool single_threaded = __libc_single_threaded;
+if (single_threaded)
+  prepare_single_threaded ();
+else
+  prepare_multi_thread ();
+
+void *handle = dlopen (shared_library_name, RTLD_NOW);
+lookup_symbols (handle);
+
+if (single_threaded)
+  cleanup_single_threaded ();
+else
+  cleanup_multi_thread ();
+@end smallexample
+
+Since the variable @code{__libc_single_threaded} can change from true
+to false during the execution of the program, it is not useful for
+selecting optimized function implementations in IFUNC resolvers.
+@end deftypevar
+
 @c FIXME these are undocumented:
 @c pthread_atfork
 @c pthread_attr_destroy
diff --git a/misc/Makefile b/misc/Makefile
index 032f28fc38..5bd78a99ac 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -37,7 +37,8 @@  headers	:= sys/uio.h bits/uio-ext.h bits/uio_lim.h \
 	   bits/syslog.h bits/syslog-ldbl.h bits/syslog-path.h bits/error.h \
 	   bits/select2.h bits/hwcap.h sys/auxv.h \
 	   sys/sysmacros.h bits/sysmacros.h bits/types/struct_iovec.h \
-	   bits/err-ldbl.h bits/error-ldbl.h
+	   bits/err-ldbl.h bits/error-ldbl.h \
+	   sys/single_threaded.h
 
 routines := brk sbrk sstk ioctl \
 	    readv writev preadv preadv64 pwritev pwritev64 \
@@ -72,7 +73,7 @@  routines := brk sbrk sstk ioctl \
 	    fgetxattr flistxattr fremovexattr fsetxattr getxattr \
 	    listxattr lgetxattr llistxattr lremovexattr lsetxattr \
 	    removexattr setxattr getauxval ifunc-impl-list makedev \
-	    allocate_once
+	    allocate_once single_threaded
 
 generated += tst-error1.mtrace tst-error1-mem.out \
   tst-allocate_once.mtrace tst-allocate_once-mem.out
diff --git a/misc/single_threaded.c b/misc/single_threaded.c
new file mode 100644
index 0000000000..2262c4512d
--- /dev/null
+++ b/misc/single_threaded.c
@@ -0,0 +1,27 @@ 
+/* Support for single-thread optimizations.  Statically linked version.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/single_threaded.h>
+
+/* For dynamically linked programs, the definition is supplied by the
+   dynamic linker.  For statically linked programs, there is just one
+   thread when the program is loaded, so it is safe to initialize it
+   early.  */
+#ifndef SHARED
+char __libc_single_threaded = 1;
+#endif
diff --git a/misc/sys/single_threaded.h b/misc/sys/single_threaded.h
new file mode 100644
index 0000000000..c721141d35
--- /dev/null
+++ b/misc/sys/single_threaded.h
@@ -0,0 +1,33 @@ 
+/* Support for single-thread optimizations.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _SYS_SINGLE_THREADED_H
+#define _SYS_SINGLE_THREADED_H
+
+#include <features.h>
+
+__BEGIN_DECLS
+
+/* If this variable is non-zero, then the current thread is the only
+   thread in the process image.  If it is zero, the process can be
+   multi-threaded.  */
+extern char __libc_single_threaded;
+
+__END_DECLS
+
+#endif /* _SYS_SINGLE_THREADED_H */
diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
index 18b7bbe765..2cfdb49699 100644
--- a/nptl/pthread_create.c
+++ b/nptl/pthread_create.c
@@ -34,6 +34,7 @@ 
 #include <futex-internal.h>
 #include <tls-setup.h>
 #include "libioP.h"
+#include <sys/single_threaded.h>
 
 #include <shlib-compat.h>
 
@@ -627,6 +628,8 @@  __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
 {
   STACK_VARIABLES;
 
+  __libc_single_threaded_create_thread ();
+
   const struct pthread_attr *iattr = (struct pthread_attr *) attr;
   struct pthread_attr default_attr;
   bool free_cpuset = false;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index b1fc5c31f9..a0145de571 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -453,9 +453,11 @@  struct rtld_global
 #   define __rtld_local_attribute__ __attribute__ ((visibility ("hidden")))
 #  endif
 extern struct rtld_global _rtld_local __rtld_local_attribute__;
+extern char __libc_single_threaded_local __rtld_local_attribute__;
 #  undef __rtld_local_attribute__
 # endif
 extern struct rtld_global _rtld_global __rtld_global_attribute__;
+extern char __libc_single_threaded __rtld_global_attribute__;
 # undef __rtld_global_attribute__
 #endif
 
@@ -1071,6 +1073,9 @@  extern struct link_map * _dl_get_dl_main_map (void)
    If libpthread is not linked in, this is an empty function.  */
 void __pthread_initialize_minimal (void) weak_function;
 
+/* Update both copies of __libc_single_threaded.  */
+void _dl_single_threaded_update (char value);
+
 /* Allocate memory for static TLS block (unless MEM is nonzero) and dtv.  */
 extern void *_dl_allocate_tls (void *mem);
 rtld_hidden_proto (_dl_allocate_tls)
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index 5869e67c91..2806262064 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -28,6 +28,7 @@ 
 #include "hurdmalloc.h"		/* XXX */
 #include <tls.h>
 #include <malloc/malloc-internal.h>
+#include <sys/single_threaded.h>
 
 #undef __fork
 
@@ -644,6 +645,8 @@  __fork (void)
       /* Forking clears the trace flag.  */
       __sigemptyset (&_hurdsig_traced);
 
+      __libc_single_threaded_subprocess_after_fork ();
+
       /* Release malloc locks.  */
       _hurd_malloc_fork_child ();
       call_function_static_weak (__malloc_fork_unlock_child);
diff --git a/sysdeps/mach/hurd/i386/ld.abilist b/sysdeps/mach/hurd/i386/ld.abilist
index c76b913486..8bda07d04d 100644
--- a/sysdeps/mach/hurd/i386/ld.abilist
+++ b/sysdeps/mach/hurd/i386/ld.abilist
@@ -22,4 +22,5 @@  GLIBC_2.2.6 malloc F
 GLIBC_2.2.6 realloc F
 GLIBC_2.3 ___tls_get_addr F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x4
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index 3f357665bd..b29abf1503 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -32,6 +32,7 @@ 
 #include <arch-fork.h>
 #include <futex-internal.h>
 #include <malloc/malloc-internal.h>
+#include <sys/single_threaded.h>
 
 static void
 fresetlockfiles (void)
@@ -109,6 +110,8 @@  __libc_fork (void)
 # endif
 #endif
 
+      __libc_single_threaded_subprocess_after_fork ();
+
       /* Reset the lock state in the multi-threaded case.  */
       if (multiple_threads)
 	{
diff --git a/sysdeps/unix/sysv/linux/aarch64/ld.abilist b/sysdeps/unix/sysv/linux/aarch64/ld.abilist
index 4ffe688649..e499b3ea17 100644
--- a/sysdeps/unix/sysv/linux/aarch64/ld.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/ld.abilist
@@ -7,3 +7,4 @@  GLIBC_2.17 calloc F
 GLIBC_2.17 free F
 GLIBC_2.17 malloc F
 GLIBC_2.17 realloc F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/alpha/ld.abilist b/sysdeps/unix/sysv/linux/alpha/ld.abilist
index 98b66edabf..2e0b99c369 100644
--- a/sysdeps/unix/sysv/linux/alpha/ld.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/ld.abilist
@@ -6,4 +6,5 @@  GLIBC_2.0 realloc F
 GLIBC_2.1 __libc_stack_end D 0x8
 GLIBC_2.1 _dl_mcount F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x8
diff --git a/sysdeps/unix/sysv/linux/arm/ld.abilist b/sysdeps/unix/sysv/linux/arm/ld.abilist
index a301c6ebc4..a3a12a9689 100644
--- a/sysdeps/unix/sysv/linux/arm/ld.abilist
+++ b/sysdeps/unix/sysv/linux/arm/ld.abilist
@@ -1,3 +1,4 @@ 
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __libc_stack_end D 0x4
 GLIBC_2.4 __stack_chk_guard D 0x4
 GLIBC_2.4 __tls_get_addr F
diff --git a/sysdeps/unix/sysv/linux/csky/ld.abilist b/sysdeps/unix/sysv/linux/csky/ld.abilist
index 71576160ed..5cee5ae3b1 100644
--- a/sysdeps/unix/sysv/linux/csky/ld.abilist
+++ b/sysdeps/unix/sysv/linux/csky/ld.abilist
@@ -7,3 +7,4 @@  GLIBC_2.29 calloc F
 GLIBC_2.29 free F
 GLIBC_2.29 malloc F
 GLIBC_2.29 realloc F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/hppa/ld.abilist b/sysdeps/unix/sysv/linux/hppa/ld.abilist
index 0387614d8f..8e8f931687 100644
--- a/sysdeps/unix/sysv/linux/hppa/ld.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/ld.abilist
@@ -6,4 +6,5 @@  GLIBC_2.2 free F
 GLIBC_2.2 malloc F
 GLIBC_2.2 realloc F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x4
diff --git a/sysdeps/unix/sysv/linux/i386/ld.abilist b/sysdeps/unix/sysv/linux/i386/ld.abilist
index edb7307228..1839498f28 100644
--- a/sysdeps/unix/sysv/linux/i386/ld.abilist
+++ b/sysdeps/unix/sysv/linux/i386/ld.abilist
@@ -7,3 +7,4 @@  GLIBC_2.1 __libc_stack_end D 0x4
 GLIBC_2.1 _dl_mcount F
 GLIBC_2.3 ___tls_get_addr F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/ia64/ld.abilist b/sysdeps/unix/sysv/linux/ia64/ld.abilist
index 82042472c3..39d9276ff4 100644
--- a/sysdeps/unix/sysv/linux/ia64/ld.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/ld.abilist
@@ -6,3 +6,4 @@  GLIBC_2.2 free F
 GLIBC_2.2 malloc F
 GLIBC_2.2 realloc F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/ld.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/ld.abilist
index a301c6ebc4..a3a12a9689 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/ld.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/ld.abilist
@@ -1,3 +1,4 @@ 
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __libc_stack_end D 0x4
 GLIBC_2.4 __stack_chk_guard D 0x4
 GLIBC_2.4 __tls_get_addr F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/ld.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/ld.abilist
index c9ec45cf1c..5a0e0a55df 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/ld.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/ld.abilist
@@ -6,4 +6,5 @@  GLIBC_2.0 realloc F
 GLIBC_2.1 __libc_stack_end D 0x4
 GLIBC_2.1 _dl_mcount F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x4
diff --git a/sysdeps/unix/sysv/linux/microblaze/ld.abilist b/sysdeps/unix/sysv/linux/microblaze/ld.abilist
index aa0d71150a..921e4c4516 100644
--- a/sysdeps/unix/sysv/linux/microblaze/ld.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/ld.abilist
@@ -7,3 +7,4 @@  GLIBC_2.18 calloc F
 GLIBC_2.18 free F
 GLIBC_2.18 malloc F
 GLIBC_2.18 realloc F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/ld.abilist b/sysdeps/unix/sysv/linux/mips/mips32/ld.abilist
index 55d48868e8..095efb14b0 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/ld.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/ld.abilist
@@ -6,4 +6,5 @@  GLIBC_2.0 realloc F
 GLIBC_2.2 __libc_stack_end D 0x4
 GLIBC_2.2 _dl_mcount F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x4
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/ld.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/ld.abilist
index 55d48868e8..095efb14b0 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/ld.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/ld.abilist
@@ -6,4 +6,5 @@  GLIBC_2.0 realloc F
 GLIBC_2.2 __libc_stack_end D 0x4
 GLIBC_2.2 _dl_mcount F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x4
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/ld.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/ld.abilist
index 44b345b7cf..6647d7fe67 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/ld.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/ld.abilist
@@ -6,4 +6,5 @@  GLIBC_2.0 realloc F
 GLIBC_2.2 __libc_stack_end D 0x8
 GLIBC_2.2 _dl_mcount F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x8
diff --git a/sysdeps/unix/sysv/linux/nios2/ld.abilist b/sysdeps/unix/sysv/linux/nios2/ld.abilist
index 110f1039fa..e56a840003 100644
--- a/sysdeps/unix/sysv/linux/nios2/ld.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/ld.abilist
@@ -7,3 +7,4 @@  GLIBC_2.21 calloc F
 GLIBC_2.21 free F
 GLIBC_2.21 malloc F
 GLIBC_2.21 realloc F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/ld.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/ld.abilist
index e8b0ea3a9b..81fd56dd7e 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/ld.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/ld.abilist
@@ -8,3 +8,4 @@  GLIBC_2.1 _dl_mcount F
 GLIBC_2.22 __tls_get_addr_opt F
 GLIBC_2.23 __parse_hwcap_and_convert_at_platform F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/ld.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/ld.abilist
index edfc9ca56f..ce008d4409 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/ld.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/ld.abilist
@@ -8,3 +8,4 @@  GLIBC_2.3 calloc F
 GLIBC_2.3 free F
 GLIBC_2.3 malloc F
 GLIBC_2.3 realloc F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/ld.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/ld.abilist
index 37c8f6684b..ca633a094a 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/ld.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/ld.abilist
@@ -8,3 +8,4 @@  GLIBC_2.17 malloc F
 GLIBC_2.17 realloc F
 GLIBC_2.22 __tls_get_addr_opt F
 GLIBC_2.23 __parse_hwcap_and_convert_at_platform F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/ld.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/ld.abilist
index b411871d06..3df6d942e1 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/ld.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/ld.abilist
@@ -7,3 +7,4 @@  GLIBC_2.27 calloc F
 GLIBC_2.27 free F
 GLIBC_2.27 malloc F
 GLIBC_2.27 realloc F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/ld.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/ld.abilist
index 0576c9575e..5c4dd58fc1 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/ld.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/ld.abilist
@@ -6,3 +6,4 @@  GLIBC_2.0 realloc F
 GLIBC_2.1 __libc_stack_end D 0x4
 GLIBC_2.1 _dl_mcount F
 GLIBC_2.3 __tls_get_offset F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/ld.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/ld.abilist
index 1fbb890d1d..ab04315bad 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/ld.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/ld.abilist
@@ -6,3 +6,4 @@  GLIBC_2.2 free F
 GLIBC_2.2 malloc F
 GLIBC_2.2 realloc F
 GLIBC_2.3 __tls_get_offset F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/sh/ld.abilist b/sysdeps/unix/sysv/linux/sh/ld.abilist
index 0387614d8f..8e8f931687 100644
--- a/sysdeps/unix/sysv/linux/sh/ld.abilist
+++ b/sysdeps/unix/sysv/linux/sh/ld.abilist
@@ -6,4 +6,5 @@  GLIBC_2.2 free F
 GLIBC_2.2 malloc F
 GLIBC_2.2 realloc F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
 GLIBC_2.4 __stack_chk_guard D 0x4
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/ld.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/ld.abilist
index fd0b33f86d..7b2ea404fd 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/ld.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/ld.abilist
@@ -6,3 +6,4 @@  GLIBC_2.0 realloc F
 GLIBC_2.1 __libc_stack_end D 0x4
 GLIBC_2.1 _dl_mcount F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/ld.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/ld.abilist
index 82042472c3..39d9276ff4 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/ld.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/ld.abilist
@@ -6,3 +6,4 @@  GLIBC_2.2 free F
 GLIBC_2.2 malloc F
 GLIBC_2.2 realloc F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/ld.abilist b/sysdeps/unix/sysv/linux/x86_64/64/ld.abilist
index 0dc9430611..faff67b5d9 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/ld.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/ld.abilist
@@ -6,3 +6,4 @@  GLIBC_2.2.5 free F
 GLIBC_2.2.5 malloc F
 GLIBC_2.2.5 realloc F
 GLIBC_2.3 __tls_get_addr F
+GLIBC_2.31 __libc_single_threaded D 0x1
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/ld.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/ld.abilist
index 80f3161586..25f97a2524 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/ld.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/ld.abilist
@@ -6,3 +6,4 @@  GLIBC_2.16 calloc F
 GLIBC_2.16 free F
 GLIBC_2.16 malloc F
 GLIBC_2.16 realloc F
+GLIBC_2.31 __libc_single_threaded D 0x1