public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 1/2] Add the __libc_single_threaded variable
@ 2020-06-24 12:59 Florian Weimer
  2020-06-24 13:03 ` [PATCH v2 2/2] manual: Document __libc_single_threaded Florian Weimer
  2020-06-30  0:52 ` [PATCH v2 1/2] Add the __libc_single_threaded variable DJ Delorie
  0 siblings, 2 replies; 8+ messages in thread
From: Florian Weimer @ 2020-06-24 12:59 UTC (permalink / raw)
  To: libc-alpha

The variable is placed in libc.so, and it can be true only in
an outer libc, not libcs loaded via dlmopen or static dlopen.
Since thread creation from inner namespaces does not work,
pthread_create can update __libc_single_threaded directly.

Using __libc_early_init and its initial flag, implementation of this
variable is very straightforward.  A future version may reset the flag
during fork (but not in an inner namespace), or after joining all
threads except one.
---
V2: Rebased on current master, resolving abilist conflicts.

 NEWS                                          |   6 +
 elf/Makefile                                  |  33 +++-
 elf/libc_early_init.c                         |   4 +
 elf/tst-single_threaded-mod1.c                |  25 +++
 elf/tst-single_threaded-mod2.c                |  25 +++
 elf/tst-single_threaded-mod3.c                |  25 +++
 elf/tst-single_threaded-mod4.c                |  25 +++
 elf/tst-single_threaded-pthread-static.c      |  86 +++++++++
 elf/tst-single_threaded-pthread.c             | 174 ++++++++++++++++++
 elf/tst-single_threaded-static-dlopen.c       |  56 ++++++
 elf/tst-single_threaded-static.c              |  29 +++
 elf/tst-single_threaded.c                     |  70 +++++++
 htl/pt-create.c                               |   5 +
 include/sys/single_threaded.h                 |   1 +
 misc/Makefile                                 |   5 +-
 misc/Versions                                 |   3 +
 misc/single_threaded.c                        |  27 +++
 misc/sys/single_threaded.h                    |  33 ++++
 nptl/pthread_create.c                         |   5 +
 sysdeps/generic/ldsodefs.h                    |   5 +
 sysdeps/generic/libc.abilist                  |   1 +
 sysdeps/mach/hurd/i386/libc.abilist           |   1 +
 sysdeps/unix/sysv/linux/aarch64/libc.abilist  |   1 +
 sysdeps/unix/sysv/linux/alpha/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/arm/be/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/arm/le/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/csky/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/hppa/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/i386/libc.abilist     |   1 +
 sysdeps/unix/sysv/linux/ia64/libc.abilist     |   1 +
 .../sysv/linux/m68k/coldfire/libc.abilist     |   1 +
 .../unix/sysv/linux/m68k/m680x0/libc.abilist  |   1 +
 .../sysv/linux/microblaze/be/libc.abilist     |   1 +
 .../sysv/linux/microblaze/le/libc.abilist     |   1 +
 .../sysv/linux/mips/mips32/fpu/libc.abilist   |   1 +
 .../sysv/linux/mips/mips32/nofpu/libc.abilist |   1 +
 .../sysv/linux/mips/mips64/n32/libc.abilist   |   1 +
 .../sysv/linux/mips/mips64/n64/libc.abilist   |   1 +
 sysdeps/unix/sysv/linux/nios2/libc.abilist    |   1 +
 .../linux/powerpc/powerpc32/fpu/libc.abilist  |   1 +
 .../powerpc/powerpc32/nofpu/libc.abilist      |   1 +
 .../linux/powerpc/powerpc64/be/libc.abilist   |   1 +
 .../linux/powerpc/powerpc64/le/libc.abilist   |   1 +
 .../unix/sysv/linux/riscv/rv64/libc.abilist   |   1 +
 .../unix/sysv/linux/s390/s390-32/libc.abilist |   1 +
 .../unix/sysv/linux/s390/s390-64/libc.abilist |   1 +
 sysdeps/unix/sysv/linux/sh/be/libc.abilist    |   1 +
 sysdeps/unix/sysv/linux/sh/le/libc.abilist    |   1 +
 .../sysv/linux/sparc/sparc32/libc.abilist     |   1 +
 .../sysv/linux/sparc/sparc64/libc.abilist     |   1 +
 .../unix/sysv/linux/x86_64/64/libc.abilist    |   1 +
 .../unix/sysv/linux/x86_64/x32/libc.abilist   |   1 +
 52 files changed, 666 insertions(+), 8 deletions(-)
 create mode 100644 elf/tst-single_threaded-mod1.c
 create mode 100644 elf/tst-single_threaded-mod2.c
 create mode 100644 elf/tst-single_threaded-mod3.c
 create mode 100644 elf/tst-single_threaded-mod4.c
 create mode 100644 elf/tst-single_threaded-pthread-static.c
 create mode 100644 elf/tst-single_threaded-pthread.c
 create mode 100644 elf/tst-single_threaded-static-dlopen.c
 create mode 100644 elf/tst-single_threaded-static.c
 create mode 100644 elf/tst-single_threaded.c
 create mode 100644 include/sys/single_threaded.h
 create mode 100644 misc/single_threaded.c
 create mode 100644 misc/sys/single_threaded.h

diff --git a/NEWS b/NEWS
index a660fc59a8..1f70b73edd 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,12 @@ Major new features:
   pthread_attr_getsigmask_np have been added.  They allow applications
   to specify the signal mask of a thread created with pthread_create.
 
+* The GNU C Library now provides the header file <sys/single_threaded.h>
+  which declares 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 deprecated <sys/sysctl.h> header and the sysctl function have been
diff --git a/elf/Makefile b/elf/Makefile
index 6fe1df90bb..81a696c3ef 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -155,7 +155,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
@@ -174,9 +176,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 \
@@ -204,7 +208,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-dlopen-self tst-auditmany tst-initfinilazyfail tst-dlopenfail \
 	 tst-dlopenfail-2 \
 	 tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen \
-	 tst-audit14 tst-audit15 tst-audit16
+	 tst-audit14 tst-audit15 tst-audit16 \
+	 tst-single_threaded tst-single_threaded-pthread
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -317,7 +322,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \
 		tst-dlopenfailmod3 tst-ldconfig-ld-mod \
 		tst-filterobj-flt tst-filterobj-aux tst-filterobj-filtee \
-		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3
+		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3 \
+		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%,\
@@ -1748,3 +1755,17 @@ $(objpfx)tst-auxobj: $(objpfx)tst-filterobj-aux.so
 $(objpfx)tst-auxobj-dlopen: $(libdl)
 $(objpfx)tst-auxobj.out: $(objpfx)tst-filterobj-filtee.so
 $(objpfx)tst-auxobj-dlopen.out: $(objpfx)tst-filterobj-filtee.so
+
+$(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/libc_early_init.c b/elf/libc_early_init.c
index e6c64fb526..725ab2f811 100644
--- a/elf/libc_early_init.c
+++ b/elf/libc_early_init.c
@@ -18,10 +18,14 @@
 
 #include <ctype.h>
 #include <libc-early-init.h>
+#include <sys/single_threaded.h>
 
 void
 __libc_early_init (_Bool initial)
 {
   /* Initialize ctype data.  */
   __ctype_init ();
+
+  /* Only the outer namespace is marked as single-threaded.  */
+  __libc_single_threaded = initial;
 }
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..c02f4047d1
--- /dev/null
+++ b/elf/tst-single_threaded-pthread.c
@@ -0,0 +1,174 @@
+/* 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;
+}
+
+/* Used for closure arguments to the subprocess function.  */
+static char expected_false = 0;
+static char expected_true = 1;
+
+/* A subprocess inherits currently inherits the single-threaded state
+   of the parent process.  */
+static void
+subprocess (void *closure)
+{
+  const char *expected = closure;
+  TEST_COMPARE (__libc_single_threaded, *expected);
+  TEST_COMPARE (single_threaded_1 (), *expected);
+  if (single_threaded_2 != NULL)
+    TEST_COMPARE (single_threaded_2 (), *expected);
+  if (single_threaded_3 != NULL)
+    TEST_COMPARE (single_threaded_3 (), *expected);
+  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, &expected_true);
+
+  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, &expected_false);
+
+  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, &expected_false);
+
+  /* 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, &expected_false);
+
+  /* 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, &expected_false);
+
+  /* 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, &expected_false);
+
+  /* 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, &expected_false);
+
+  /* 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..f270cf452e
--- /dev/null
+++ b/elf/tst-single_threaded-static-dlopen.c
@@ -0,0 +1,56 @@
+/* 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 ());
+  /* The inner libc always assumes multi-threaded use.  */
+  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..478c2dc259
--- /dev/null
+++ b/elf/tst-single_threaded.c
@@ -0,0 +1,70 @@
+/* Test support for single-thread optimizations.  No threads.
+   Copyright (C) 2020 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 treats the inner namespace as
+     multi-threaded.  */
+  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 f501a12017..7ac875cbf7 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>
@@ -104,6 +105,10 @@ __pthread_create_internal (struct __pthread **thread,
   sigset_t sigset;
   size_t stacksize;
 
+  /* Avoid a data race in the multi-threaded case.  */
+  if (__libc_single_threaded)
+    __libc_single_threaded = 0;
+
   /* 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..18f6972482
--- /dev/null
+++ b/include/sys/single_threaded.h
@@ -0,0 +1 @@
+#include <misc/sys/single_threaded.h>
diff --git a/misc/Makefile b/misc/Makefile
index 67c5237f97..58959f6913 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 fd_to_filename
+	    allocate_once fd_to_filename single_threaded
 
 generated += tst-error1.mtrace tst-error1-mem.out \
   tst-allocate_once.mtrace tst-allocate_once-mem.out
diff --git a/misc/Versions b/misc/Versions
index e749582369..95666f6548 100644
--- a/misc/Versions
+++ b/misc/Versions
@@ -161,6 +161,9 @@ libc {
   GLIBC_2.30 {
     twalk_r;
   }
+  GLIBC_2.32 {
+    __libc_single_threaded;
+  }
   GLIBC_PRIVATE {
     __madvise;
     __mktemp;
diff --git a/misc/single_threaded.c b/misc/single_threaded.c
new file mode 100644
index 0000000000..d7c55b784b
--- /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>
+
+/* In dynamically linked programs, this variable is initialized in
+   __libc_early_init (as false for inner libcs).  */
+#ifdef SHARED
+char __libc_single_threaded;
+#else
+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 35a9927cf2..6dac80da43 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>
 
@@ -611,6 +612,10 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
 {
   STACK_VARIABLES;
 
+  /* Avoid a data race in the multi-threaded case.  */
+  if (__libc_single_threaded)
+    __libc_single_threaded = 0;
+
   const struct pthread_attr *iattr = (struct pthread_attr *) attr;
   union pthread_attr_transparent default_attr;
   bool destroy_default_attr = false;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index d08b97a5ef..85a0343ce1 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -482,9 +482,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
 
@@ -1119,6 +1121,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/generic/libc.abilist b/sysdeps/generic/libc.abilist
index e69de29bb2..8ca9b93c2f 100644
--- a/sysdeps/generic/libc.abilist
+++ b/sysdeps/generic/libc.abilist
@@ -0,0 +1 @@
+GLIBC_2.32 __libc_single_threaded D 0x1
diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist
index 6400885d1d..ea985b0e41 100644
--- a/sysdeps/mach/hurd/i386/libc.abilist
+++ b/sysdeps/mach/hurd/i386/libc.abilist
@@ -2181,6 +2181,7 @@ GLIBC_2.3.4 setsourcefilter F
 GLIBC_2.3.4 xdr_quad_t F
 GLIBC_2.3.4 xdr_u_quad_t F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 mach_print F
 GLIBC_2.32 mremap F
 GLIBC_2.32 thrd_current F
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index 48c790b15d..f396aa8257 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2149,6 +2149,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index cb70cb974f..bca7dc6ffa 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -2231,6 +2231,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
index 573eca117e..6b3d3f1973 100644
--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist
@@ -133,6 +133,7 @@ GLIBC_2.30 twalk_r F
 GLIBC_2.31 msgctl F
 GLIBC_2.31 semctl F
 GLIBC_2.31 shmctl F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
index 8a8633f0a4..52bdd346c5 100644
--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist
@@ -130,6 +130,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist
index 3042a93084..8b1c8e5aef 100644
--- a/sysdeps/unix/sysv/linux/csky/libc.abilist
+++ b/sysdeps/unix/sysv/linux/csky/libc.abilist
@@ -2093,6 +2093,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist
index a02a576321..f8addf8f3d 100644
--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist
+++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist
@@ -2052,6 +2052,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index f0b9c9e070..f9745c99fc 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2218,6 +2218,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist
index 1534fd7a24..3b0db278ef 100644
--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist
@@ -2084,6 +2084,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index 9a0ada4b52..27891bfa4e 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -134,6 +134,7 @@ GLIBC_2.30 twalk_r F
 GLIBC_2.31 msgctl F
 GLIBC_2.31 semctl F
 GLIBC_2.31 shmctl F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 333c35bf16..cf87374571 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -2164,6 +2164,7 @@ GLIBC_2.30 twalk_r F
 GLIBC_2.31 msgctl F
 GLIBC_2.31 semctl F
 GLIBC_2.31 shmctl F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
index 824eceec11..acbc32d2de 100644
--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist
@@ -2144,6 +2144,7 @@ GLIBC_2.30 twalk_r F
 GLIBC_2.31 msgctl F
 GLIBC_2.31 semctl F
 GLIBC_2.31 shmctl F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
index 5a6dcdd21b..6e2ca48730 100644
--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist
@@ -2141,6 +2141,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 6e5dbb28f1..13de7d98a1 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -2135,6 +2135,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index 3ee64614b2..a788ed2fe4 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -2133,6 +2133,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index dc62615524..64a52f1d23 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -2141,6 +2141,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index 8cf78bcf51..d66e7e4d6a 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -2135,6 +2135,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist
index 7817aeb0e2..9b112ada15 100644
--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist
+++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist
@@ -2182,6 +2182,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index ca04e8f2d3..1d9852fefc 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -2191,6 +2191,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index 10cb895639..8d6839d003 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -2224,6 +2224,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
index 96ddc448d7..9f283d5f4e 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist
@@ -2054,6 +2054,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
index deddb53d83..3a4ffb921c 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist
@@ -2279,6 +2279,7 @@ GLIBC_2.32 __isoc99_vsscanfieee128 F
 GLIBC_2.32 __isoc99_vswscanfieee128 F
 GLIBC_2.32 __isoc99_vwscanfieee128 F
 GLIBC_2.32 __isoc99_wscanfieee128 F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 __obstack_printf_chkieee128 F
 GLIBC_2.32 __obstack_printfieee128 F
 GLIBC_2.32 __obstack_vprintf_chkieee128 F
diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
index 58217dcb13..25c9b14d89 100644
--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist
@@ -2111,6 +2111,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index c22c29b35a..20d84d66d3 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -2189,6 +2189,7 @@ GLIBC_2.30 twalk_r F
 GLIBC_2.31 msgctl F
 GLIBC_2.31 semctl F
 GLIBC_2.31 shmctl F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 568f1727c4..9f3aa1eef5 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -2090,6 +2090,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
index d9988dae90..95a2e032d2 100644
--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist
@@ -2059,6 +2059,7 @@ GLIBC_2.30 twalk_r F
 GLIBC_2.31 msgctl F
 GLIBC_2.31 semctl F
 GLIBC_2.31 shmctl F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
index 39edeffe82..f8729f35dc 100644
--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist
@@ -2056,6 +2056,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 8668e15e8c..b3bd8c85e4 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -2180,6 +2180,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index eb884afa3e..3381d19588 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -2107,6 +2107,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index a208fb3556..0053e972d7 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -2065,6 +2065,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 3eca3493e2..dd11be51c0 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2162,6 +2162,7 @@ GLIBC_2.30 getdents64 F
 GLIBC_2.30 gettid F
 GLIBC_2.30 tgkill F
 GLIBC_2.30 twalk_r F
+GLIBC_2.32 __libc_single_threaded D 0x1
 GLIBC_2.32 pthread_attr_getsigmask_np F
 GLIBC_2.32 pthread_attr_setaffinity_np F
 GLIBC_2.32 pthread_attr_setsigmask_np F
-- 
2.25.4



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 2/2] manual: Document __libc_single_threaded
  2020-06-24 12:59 [PATCH v2 1/2] Add the __libc_single_threaded variable Florian Weimer
@ 2020-06-24 13:03 ` Florian Weimer
  2020-06-25 12:08   ` Szabolcs Nagy
  2020-06-30  1:06   ` DJ Delorie
  2020-06-30  0:52 ` [PATCH v2 1/2] Add the __libc_single_threaded variable DJ Delorie
  1 sibling, 2 replies; 8+ messages in thread
From: Florian Weimer @ 2020-06-24 13:03 UTC (permalink / raw)
  To: libc-alpha; +Cc: Michael Kerrisk

---
V2: Fix typos.  Add the requestion documentation of implementation details.

 manual/threads.texi | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/manual/threads.texi b/manual/threads.texi
index bb7a42c655..009026deb8 100644
--- a/manual/threads.texi
+++ b/manual/threads.texi
@@ -628,6 +628,7 @@ the standard.
 * Initial Thread Signal Mask::            Setting the initial mask of threads.
 * Waiting with Explicit Clocks::          Functions for waiting with an
                                           explicit clock specification.
+* Single-Threaded::     Detecting single-threaded execution.
 @end menu
 
 @node Default Thread Attributes
@@ -843,6 +844,118 @@ Behaves like @code{pthread_timedjoin_np} except that the absolute time in
 @var{abstime} is measured against the clock specified by @var{clockid}.
 @end deftypefun
 
+@node Single-Threaded
+@subsubsection 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.  @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 may 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.
+@end deftypevar
+
+Most applications should perform the same actions whether or not
+@code{__libc_single_threaded} is true, except with less
+synchronization.  If this rule is followed, a process that
+subsequently becomes multi-threaded is already in a consistent state.
+For example, in order to increment a reference count, the following
+code can be used:
+
+@smallexample
+if (__libc_single_threaded)
+  atomic_fetch_add (&reference_count, 1, memory_order_relaxed);
+else
+  atomic_fetch_add (&reference_count, 1, memory_order_acq_rel);
+@end smallexample
+
+@c Note: No memory order on __libc_single_threaded.  The
+@c implementation must ensure that exit of the critical
+@c (second-to-last) thread happens-before setting
+@c __libc_single_threaded to true.  Otherwise, acquire MO might be
+@c needed for reading the variable in some scenarios, and that would
+@c completely defeat its purpose.
+
+This still requires some form of synchronization on the
+single-threaded branch, so it can be beneficial not to declare the
+reference count as @code{_Atomic}, and use the GCC @code{__atomic}
+built-ins.  @xref{__atomic Builtins,, Built-in Functions for Memory
+Model Aware Atomic Operations, gcc, Using the GNU Compiler Collection
+(GCC)}.  Then the code to increment a reference count looks like this:
+
+@smallexample
+if (__libc_single_threaded)
+  ++refeference_count;
+else
+  __atomic_fetch_add (&reference_count, 1, __ATOMIC_ACQ_REL);
+@end smallexample
+
+(Depending on the data associated with the reference count, it may be
+possible to use the weaker @code{__ATOMIC_RELAXED} memory ordering on
+the multi-threaded branch.)
+
+Several functions in @theglibc{} can change the value of the
+@code{__libc_single_threaded} variable.  For example, creating new
+threads using the @code{pthread_create} or @code{thrd_create} function
+sets the variable to false.  This can also happen indirectly, say via
+a call to @code{dlopen}.  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 value of @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.
+
+Atomic operations can also be used on mappings shared among
+single-threaded processes.  This means that a compiler cannot use
+@code{__libc_single_threaded} to optimize atomic operations, unless it
+is able to prove that the memory is not shared.
+
+@strong{Implementation Note:} The @code{__libc_single_threaded}
+variable is not declared as @code{volatile} because it is expected
+that compilers optimize a sequence of single-threaded checks into one
+check, for example if several reference counts are updated.  The
+current implementation in @theglibc{} does not set the
+@code{__libc_single_threaded} variable to a true value if a process
+turns single-threaded again.  Future versions of @theglibc{} may do
+this, but only as the result of function calls which imply an acquire
+(compiler) barrier.  (Some compilers assume that well-known functions
+such as @code{malloc} do not write to global variables, and setting
+@code{__libc_single_threaded} would introduce a data race and
+undefined behavior.)  In any case, an application must not write to
+@code{__libc_single_threaded} even if it has joined the last
+application-created thread because future versions of @theglibc{} may
+create background threads after the first thread has been created, and
+the application has no way of knowning that these threads are present.
+
 @c FIXME these are undocumented:
 @c pthread_atfork
 @c pthread_attr_destroy


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/2] manual: Document __libc_single_threaded
  2020-06-24 13:03 ` [PATCH v2 2/2] manual: Document __libc_single_threaded Florian Weimer
@ 2020-06-25 12:08   ` Szabolcs Nagy
  2020-06-30  8:56     ` Florian Weimer
  2020-06-30  1:06   ` DJ Delorie
  1 sibling, 1 reply; 8+ messages in thread
From: Szabolcs Nagy @ 2020-06-25 12:08 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha, Michael Kerrisk, Rich Felker

The 06/24/2020 15:03, Florian Weimer via Libc-alpha wrote:
> ---
> V2: Fix typos.  Add the requestion documentation of implementation details.

this spec looks ok to me.

Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>

one comment:

linux has several syscalls that change some property of
the current thread and get inherited by new threads
(e.g. setxid, various prctls, signal mask) and a user
may want to set such property for the entire process
and bail out if that cannot work, so the question is
if __libc_single_threaded works for this? even if this
is done in a signal handler? e.g.

if (__libc_single_thread)
  prctl(set_property,a2,a3,a4,a5); // entire process ok
else
  bailout(); // there might be other threads

i think this may be another valid usage (not related
to atomics).

> 
>  manual/threads.texi | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 113 insertions(+)
> 
> diff --git a/manual/threads.texi b/manual/threads.texi
> index bb7a42c655..009026deb8 100644
> --- a/manual/threads.texi
> +++ b/manual/threads.texi
> @@ -628,6 +628,7 @@ the standard.
>  * Initial Thread Signal Mask::            Setting the initial mask of threads.
>  * Waiting with Explicit Clocks::          Functions for waiting with an
>                                            explicit clock specification.
> +* Single-Threaded::     Detecting single-threaded execution.
>  @end menu
>  
>  @node Default Thread Attributes
> @@ -843,6 +844,118 @@ Behaves like @code{pthread_timedjoin_np} except that the absolute time in
>  @var{abstime} is measured against the clock specified by @var{clockid}.
>  @end deftypefun
>  
> +@node Single-Threaded
> +@subsubsection 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.  @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 may 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.
> +@end deftypevar
> +
> +Most applications should perform the same actions whether or not
> +@code{__libc_single_threaded} is true, except with less
> +synchronization.  If this rule is followed, a process that
> +subsequently becomes multi-threaded is already in a consistent state.
> +For example, in order to increment a reference count, the following
> +code can be used:
> +
> +@smallexample
> +if (__libc_single_threaded)
> +  atomic_fetch_add (&reference_count, 1, memory_order_relaxed);
> +else
> +  atomic_fetch_add (&reference_count, 1, memory_order_acq_rel);
> +@end smallexample
> +
> +@c Note: No memory order on __libc_single_threaded.  The
> +@c implementation must ensure that exit of the critical
> +@c (second-to-last) thread happens-before setting
> +@c __libc_single_threaded to true.  Otherwise, acquire MO might be
> +@c needed for reading the variable in some scenarios, and that would
> +@c completely defeat its purpose.
> +
> +This still requires some form of synchronization on the
> +single-threaded branch, so it can be beneficial not to declare the
> +reference count as @code{_Atomic}, and use the GCC @code{__atomic}
> +built-ins.  @xref{__atomic Builtins,, Built-in Functions for Memory
> +Model Aware Atomic Operations, gcc, Using the GNU Compiler Collection
> +(GCC)}.  Then the code to increment a reference count looks like this:
> +
> +@smallexample
> +if (__libc_single_threaded)
> +  ++refeference_count;
> +else
> +  __atomic_fetch_add (&reference_count, 1, __ATOMIC_ACQ_REL);
> +@end smallexample
> +
> +(Depending on the data associated with the reference count, it may be
> +possible to use the weaker @code{__ATOMIC_RELAXED} memory ordering on
> +the multi-threaded branch.)
> +
> +Several functions in @theglibc{} can change the value of the
> +@code{__libc_single_threaded} variable.  For example, creating new
> +threads using the @code{pthread_create} or @code{thrd_create} function
> +sets the variable to false.  This can also happen indirectly, say via
> +a call to @code{dlopen}.  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 value of @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.
> +
> +Atomic operations can also be used on mappings shared among
> +single-threaded processes.  This means that a compiler cannot use
> +@code{__libc_single_threaded} to optimize atomic operations, unless it
> +is able to prove that the memory is not shared.
> +
> +@strong{Implementation Note:} The @code{__libc_single_threaded}
> +variable is not declared as @code{volatile} because it is expected
> +that compilers optimize a sequence of single-threaded checks into one
> +check, for example if several reference counts are updated.  The
> +current implementation in @theglibc{} does not set the
> +@code{__libc_single_threaded} variable to a true value if a process
> +turns single-threaded again.  Future versions of @theglibc{} may do
> +this, but only as the result of function calls which imply an acquire
> +(compiler) barrier.  (Some compilers assume that well-known functions
> +such as @code{malloc} do not write to global variables, and setting
> +@code{__libc_single_threaded} would introduce a data race and
> +undefined behavior.)  In any case, an application must not write to
> +@code{__libc_single_threaded} even if it has joined the last
> +application-created thread because future versions of @theglibc{} may
> +create background threads after the first thread has been created, and
> +the application has no way of knowning that these threads are present.
> +
>  @c FIXME these are undocumented:
>  @c pthread_atfork
>  @c pthread_attr_destroy
> 

-- 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/2] Add the __libc_single_threaded variable
  2020-06-24 12:59 [PATCH v2 1/2] Add the __libc_single_threaded variable Florian Weimer
  2020-06-24 13:03 ` [PATCH v2 2/2] manual: Document __libc_single_threaded Florian Weimer
@ 2020-06-30  0:52 ` DJ Delorie
  2020-06-30  9:24   ` Florian Weimer
  1 sibling, 1 reply; 8+ messages in thread
From: DJ Delorie @ 2020-06-30  0:52 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha


Nits noted below:

* comment in test about multi vs single threaded
* copyright dates still say 2019 throughout
* suggested comment change in single_threaded.h
* __libc_single_threaded_local ?
* _dl_single_threaded_update ?

Florian Weimer via Libc-alpha <libc-alpha@sourceware.org> writes:
> The variable is placed in libc.so, and it can be true only in
> an outer libc, not libcs loaded via dlmopen or static dlopen.
> Since thread creation from inner namespaces does not work,
> pthread_create can update __libc_single_threaded directly.

So we have a One True Variable in the outer glibc that starts at "1" and
gets set to "0" on thread creation; if you dlopen another glibc, the
variable therein can't be trusted but you can't use that glibc for
thread creation anyway.

Ok :-)

> diff --git a/NEWS b/NEWS
> +* The GNU C Library now provides the header file <sys/single_threaded.h>
> +  which declares 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.

Ok.

> diff --git a/elf/Makefile b/elf/Makefile
> -	       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
> +

Adds two tests.  Ok.

> -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)

New test.  Breaking out common definitions; ok.

> @@ -204,7 +208,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
>  	 tst-dlopen-self tst-auditmany tst-initfinilazyfail tst-dlopenfail \
>  	 tst-dlopenfail-2 \
>  	 tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen \
> -	 tst-audit14 tst-audit15 tst-audit16
> +	 tst-audit14 tst-audit15 tst-audit16 \
> +	 tst-single_threaded tst-single_threaded-pthread

Adds two new test.  Ok. 

> @@ -317,7 +322,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
>  		tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 \
>  		tst-dlopenfailmod3 tst-ldconfig-ld-mod \
>  		tst-filterobj-flt tst-filterobj-aux tst-filterobj-filtee \
> -		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3
> +		tst-auditlogmod-1 tst-auditlogmod-2 tst-auditlogmod-3 \
> +		tst-single_threaded-mod1 tst-single_threaded-mod2 \
> +		tst-single_threaded-mod3 tst-single_threaded-mod4

Adds four more modules.  Ok.

> +
> +$(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)

four test deps, three .out deps; ok.  Static .out doesn't need deps on
dlopen'd objects ;-)

> +#include <sys/single_threaded.h>
>  
>  void
>  __libc_early_init (_Bool initial)
>  {
>    /* Initialize ctype data.  */
>    __ctype_init ();
> +
> +  /* Only the outer namespace is marked as single-threaded.  */
> +  __libc_single_threaded = initial;
>  }

"initial" is only set if it's the outermost glibc; thus
__libc_single_threaded is TRUE for that case, and FALSE otherwise.

Ok.

> diff --git a/elf/tst-single_threaded-mod1.c b/elf/tst-single_threaded-mod1.c
> +#include <sys/single_threaded.h>
> +
> +_Bool
> +single_threaded_1 (void)
> +{
> +  return __libc_single_threaded;
> +}

Ok.

> diff --git a/elf/tst-single_threaded-mod2.c b/elf/tst-single_threaded-mod2.c
> +#include <sys/single_threaded.h>
> +
> +_Bool
> +single_threaded_2 (void)
> +{
> +  return __libc_single_threaded;
> +}

Ok.

> diff --git a/elf/tst-single_threaded-mod3.c b/elf/tst-single_threaded-mod3.c
> +#include <sys/single_threaded.h>
> +
> +_Bool
> +single_threaded_3 (void)
> +{
> +  return __libc_single_threaded;
> +}

Ok.

> diff --git a/elf/tst-single_threaded-mod4.c b/elf/tst-single_threaded-mod4.c
> +#include <sys/single_threaded.h>
> +
> +_Bool
> +single_threaded_4 (void)
> +{
> +  return __libc_single_threaded;
> +}

Ok.  Starting to see a pattern here...   ;-)

> diff --git a/elf/tst-single_threaded-pthread-static.c b/elf/tst-single_threaded-pthread-static.c
> +/* 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>

Verfies __libc_single_threaded until first pthread_create, then
everything should be !__libc_single_threaded as long as any threads
remain unjoined.  Ok.

> diff --git a/elf/tst-single_threaded-pthread.c b/elf/tst-single_threaded-pthread.c
> new file mode 100644
> index 0000000000..c02f4047d1
> --- /dev/null
> +++ b/elf/tst-single_threaded-pthread.c
> @@ -0,0 +1,174 @@
> +/* Test support for single-thread optimizations.  With threads.
> +   Copyright (C) 2019 Free Software Foundation, Inc.

It's 2020 now.  Same throughout.

> +   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;
> +}

All check for signalling multi-threaded.  Ok.

> +/* Used for closure arguments to the subprocess function.  */
> +static char expected_false = 0;
> +static char expected_true = 1;
> +
> +/* A subprocess inherits currently inherits the single-threaded state
> +   of the parent process.  */
> +static void
> +subprocess (void *closure)
> +{
> +  const char *expected = closure;
> +  TEST_COMPARE (__libc_single_threaded, *expected);
> +  TEST_COMPARE (single_threaded_1 (), *expected);
> +  if (single_threaded_2 != NULL)
> +    TEST_COMPARE (single_threaded_2 (), *expected);
> +  if (single_threaded_3 != NULL)
> +    TEST_COMPARE (single_threaded_3 (), *expected);
> +  if (single_threaded_4 != NULL)
> +    TEST_VERIFY (!single_threaded_4 ());
> +}

Ok.

> +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, &expected_true);

Test for signalling single threaded.  Ok.

> +  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 ());

Loading an SO doesn't make it multi threaded.  Ok.

> +  /* 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, &expected_false);

Now we expect signalling multi-threaded.  Ok.

> +  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, &expected_false);
> +
> +  /* 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, &expected_false);
> +
> +  /* 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, &expected_false);
> +
> +  /* 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, &expected_false);
> +
> +  /* 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, &expected_false);

Likewise for all these.  Ok.

> +  /* 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>

Ok.

> diff --git a/elf/tst-single_threaded-static-dlopen.c b/elf/tst-single_threaded-static-dlopen.c
> new file mode 100644
> index 0000000000..f270cf452e
> --- /dev/null
> +++ b/elf/tst-single_threaded-static-dlopen.c
> @@ -0,0 +1,56 @@
> +/* 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 ());

You say "assume multi-threaded" but you test for signalling single
threaded.

> +  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 ());
> +  /* The inner libc always assumes multi-threaded use.  */
> +  TEST_VERIFY (!single_threaded_2 ());

Ok.

> diff --git a/elf/tst-single_threaded-static.c b/elf/tst-single_threaded-static.c
> +#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>

Ok.

> diff --git a/elf/tst-single_threaded.c b/elf/tst-single_threaded.c
> +#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 ());
> +}

Ok.

> +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 treats the inner namespace as
> +     multi-threaded.  */
> +  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>

Ok.

> diff --git a/htl/pt-create.c b/htl/pt-create.c
> index f501a12017..7ac875cbf7 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>
> @@ -104,6 +105,10 @@ __pthread_create_internal (struct __pthread **thread,
>    sigset_t sigset;
>    size_t stacksize;
>  
> +  /* Avoid a data race in the multi-threaded case.  */
> +  if (__libc_single_threaded)
> +    __libc_single_threaded = 0;

The only time you don't set __libc_single_threaded to zero is if it's
already zero, so the test isn't needed.  Just setting
__libc_single_threaded to zero does the same thing, but faster.  Even if
we're multi-threaded, it doesn't matter if other cpus get the old value
(zero) or the new one (zero).

The comment doesn't really enlighten the reader, IMHO.

> diff --git a/include/sys/single_threaded.h b/include/sys/single_threaded.h
> +#include <misc/sys/single_threaded.h>

Ok.

> diff --git a/misc/Makefile b/misc/Makefile
> index 67c5237f97..58959f6913 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 fd_to_filename
> +	    allocate_once fd_to_filename single_threaded

Ok.

> diff --git a/misc/Versions b/misc/Versions
> +  GLIBC_2.32 {
> +    __libc_single_threaded;
> +  }

Ok.

> diff --git a/misc/single_threaded.c b/misc/single_threaded.c
> +#include <sys/single_threaded.h>
> +
> +/* In dynamically linked programs, this variable is initialized in
> +   __libc_early_init (as false for inner libcs).  */
> +#ifdef SHARED
> +char __libc_single_threaded;
> +#else
> +char __libc_single_threaded = 1;
> +#endif

Ok.  Default for SHARED is assume multi-threaded unless properly
initialized.

> diff --git a/misc/sys/single_threaded.h b/misc/sys/single_threaded.h
> +#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 */

Ok.  Suggest s/can be/might be/.  "Can" implies we're giving the user
permission to do something, "might" indicates a condition to be
expected.  Or "the process must act as if multiple threads are running." ?

> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
> +#include <sys/single_threaded.h>
>  
>  #include <shlib-compat.h>
>  
> @@ -611,6 +612,10 @@ __pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
>  {
>    STACK_VARIABLES;
>  
> +  /* Avoid a data race in the multi-threaded case.  */
> +  if (__libc_single_threaded)
> +    __libc_single_threaded = 0;

Same comment as last time.

> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
>  extern struct rtld_global _rtld_local __rtld_local_attribute__;
> +extern char __libc_single_threaded_local __rtld_local_attribute__;

This isn't mentioned anywhere else...

>  #  undef __rtld_local_attribute__
>  # endif
>  extern struct rtld_global _rtld_global __rtld_global_attribute__;
> +extern char __libc_single_threaded __rtld_global_attribute__;

Ok.

> @@ -1119,6 +1121,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);
> +

Nor is this mentioned elsewhere.

> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1
> +GLIBC_2.32 __libc_single_threaded D 0x1

Ok.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/2] manual: Document __libc_single_threaded
  2020-06-24 13:03 ` [PATCH v2 2/2] manual: Document __libc_single_threaded Florian Weimer
  2020-06-25 12:08   ` Szabolcs Nagy
@ 2020-06-30  1:06   ` DJ Delorie
  2020-06-30  8:57     ` Florian Weimer
  1 sibling, 1 reply; 8+ messages in thread
From: DJ Delorie @ 2020-06-30  1:06 UTC (permalink / raw)
  To: Florian Weimer; +Cc: libc-alpha, mtk.manpages


LGTM but one optional wording change near the end.

Reviewed-by: DJ Delorie <dj@redhat.com>

Florian Weimer via Libc-alpha <libc-alpha@sourceware.org> writes:
> +* Single-Threaded::     Detecting single-threaded execution.

Ok.

>  @var{abstime} is measured against the clock specified by @var{clockid}.
>  @end deftypefun
>  
> +@node Single-Threaded
> +@subsubsection 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.  @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 may 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.
> +@end deftypevar

Ok.

> +Most applications should perform the same actions whether or not
> +@code{__libc_single_threaded} is true, except with less
> +synchronization.  If this rule is followed, a process that
> +subsequently becomes multi-threaded is already in a consistent state.
> +For example, in order to increment a reference count, the following
> +code can be used:

Ok.

> +@smallexample
> +if (__libc_single_threaded)
> +  atomic_fetch_add (&reference_count, 1, memory_order_relaxed);
> +else
> +  atomic_fetch_add (&reference_count, 1, memory_order_acq_rel);
> +@end smallexample

Ok.

> +@c Note: No memory order on __libc_single_threaded.  The
> +@c implementation must ensure that exit of the critical
> +@c (second-to-last) thread happens-before setting
> +@c __libc_single_threaded to true.  Otherwise, acquire MO might be
> +@c needed for reading the variable in some scenarios, and that would
> +@c completely defeat its purpose.
> +
> +This still requires some form of synchronization on the
> +single-threaded branch, so it can be beneficial not to declare the
> +reference count as @code{_Atomic}, and use the GCC @code{__atomic}
> +built-ins.  @xref{__atomic Builtins,, Built-in Functions for Memory
> +Model Aware Atomic Operations, gcc, Using the GNU Compiler Collection
> +(GCC)}.  Then the code to increment a reference count looks like this:
> +
> +@smallexample
> +if (__libc_single_threaded)
> +  ++refeference_count;
> +else
> +  __atomic_fetch_add (&reference_count, 1, __ATOMIC_ACQ_REL);
> +@end smallexample

Ok.

> +(Depending on the data associated with the reference count, it may be
> +possible to use the weaker @code{__ATOMIC_RELAXED} memory ordering on
> +the multi-threaded branch.)
> +
> +Several functions in @theglibc{} can change the value of the
> +@code{__libc_single_threaded} variable.  For example, creating new
> +threads using the @code{pthread_create} or @code{thrd_create} function
> +sets the variable to false.  This can also happen indirectly, say via
> +a call to @code{dlopen}.  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

Ok.

> +Since the value of @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.

Ok.

> +Atomic operations can also be used on mappings shared among
> +single-threaded processes.  This means that a compiler cannot use
> +@code{__libc_single_threaded} to optimize atomic operations, unless it
> +is able to prove that the memory is not shared.

Ok.  "must not" would be slightly more correct than "cannot" though.

> +@strong{Implementation Note:} The @code{__libc_single_threaded}
> +variable is not declared as @code{volatile} because it is expected
> +that compilers optimize a sequence of single-threaded checks into one
> +check, for example if several reference counts are updated.  The
> +current implementation in @theglibc{} does not set the
> +@code{__libc_single_threaded} variable to a true value if a process
> +turns single-threaded again.  Future versions of @theglibc{} may do
> +this, but only as the result of function calls which imply an acquire
> +(compiler) barrier.  (Some compilers assume that well-known functions
> +such as @code{malloc} do not write to global variables, and setting
> +@code{__libc_single_threaded} would introduce a data race and
> +undefined behavior.)  In any case, an application must not write to
> +@code{__libc_single_threaded} even if it has joined the last
> +application-created thread because future versions of @theglibc{} may
> +create background threads after the first thread has been created, and
> +the application has no way of knowning that these threads are present.
> +
>  @c FIXME these are undocumented:
>  @c pthread_atfork
>  @c pthread_attr_destroy

Ok.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/2] manual: Document __libc_single_threaded
  2020-06-25 12:08   ` Szabolcs Nagy
@ 2020-06-30  8:56     ` Florian Weimer
  0 siblings, 0 replies; 8+ messages in thread
From: Florian Weimer @ 2020-06-30  8:56 UTC (permalink / raw)
  To: Szabolcs Nagy; +Cc: libc-alpha, Michael Kerrisk, Rich Felker

* Szabolcs Nagy:

> linux has several syscalls that change some property of
> the current thread and get inherited by new threads
> (e.g. setxid, various prctls, signal mask) and a user
> may want to set such property for the entire process
> and bail out if that cannot work, so the question is
> if __libc_single_threaded works for this? even if this
> is done in a signal handler? e.g.
>
> if (__libc_single_thread)
>   prctl(set_property,a2,a3,a4,a5); // entire process ok
> else
>   bailout(); // there might be other threads
>
> i think this may be another valid usage (not related
> to atomics).

In theory, that could be the case, but the question is what you do in
the bailout case.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/2] manual: Document __libc_single_threaded
  2020-06-30  1:06   ` DJ Delorie
@ 2020-06-30  8:57     ` Florian Weimer
  0 siblings, 0 replies; 8+ messages in thread
From: Florian Weimer @ 2020-06-30  8:57 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha, mtk.manpages

* DJ Delorie:

>> +Atomic operations can also be used on mappings shared among
>> +single-threaded processes.  This means that a compiler cannot use
>> +@code{__libc_single_threaded} to optimize atomic operations, unless it
>> +is able to prove that the memory is not shared.
>
> Ok.  "must not" would be slightly more correct than "cannot" though.

Thanks, applied.

Florian


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/2] Add the __libc_single_threaded variable
  2020-06-30  0:52 ` [PATCH v2 1/2] Add the __libc_single_threaded variable DJ Delorie
@ 2020-06-30  9:24   ` Florian Weimer
  0 siblings, 0 replies; 8+ messages in thread
From: Florian Weimer @ 2020-06-30  9:24 UTC (permalink / raw)
  To: DJ Delorie; +Cc: libc-alpha

* DJ Delorie:

>> diff --git a/elf/tst-single_threaded-pthread.c b/elf/tst-single_threaded-pthread.c
>> new file mode 100644
>> index 0000000000..c02f4047d1
>> --- /dev/null
>> +++ b/elf/tst-single_threaded-pthread.c
>> @@ -0,0 +1,174 @@
>> +/* Test support for single-thread optimizations.  With threads.
>> +   Copyright (C) 2019 Free Software Foundation, Inc.
>
> It's 2020 now.  Same throughout.

Right, I also changed http:// to https:// throughout.

>> diff --git a/elf/tst-single_threaded-static-dlopen.c b/elf/tst-single_threaded-static-dlopen.c
>> new file mode 100644
>> index 0000000000..f270cf452e
>> --- /dev/null
>> +++ b/elf/tst-single_threaded-static-dlopen.c
>> @@ -0,0 +1,56 @@

>> +
>> +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 ());
>
> You say "assume multi-threaded" but you test for signalling single
> threaded.

Fixed.

>> diff --git a/htl/pt-create.c b/htl/pt-create.c
>> index f501a12017..7ac875cbf7 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>
>> @@ -104,6 +105,10 @@ __pthread_create_internal (struct __pthread **thread,
>>    sigset_t sigset;
>>    size_t stacksize;
>>  
>> +  /* Avoid a data race in the multi-threaded case.  */
>> +  if (__libc_single_threaded)
>> +    __libc_single_threaded = 0;
>
> The only time you don't set __libc_single_threaded to zero is if it's
> already zero, so the test isn't needed.  Just setting
> __libc_single_threaded to zero does the same thing, but faster.  Even if
> we're multi-threaded, it doesn't matter if other cpus get the old value
> (zero) or the new one (zero).
>
> The comment doesn't really enlighten the reader, IMHO.

If a variable is written in one thread and read in another thread
without synchronization, this is a data race, even if the value written
equals what's already in the variable.  Under the C11 memory model, the
condition is actually required to avoid undefined behavior (due to the
data race).

The condition also avoids polluting the cache line if the expected value
is already there, so it's probably faster anyway (although it will be
difficult to show the effect).

>> diff --git a/misc/sys/single_threaded.h b/misc/sys/single_threaded.h
>> +#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 */
>
> Ok.  Suggest s/can be/might be/.  "Can" implies we're giving the user
> permission to do something, "might" indicates a condition to be
> expected.  Or "the process must act as if multiple threads are running." ?

I switched to “might”.

>> diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
>>  extern struct rtld_global _rtld_local __rtld_local_attribute__;
>> +extern char __libc_single_threaded_local __rtld_local_attribute__;
>
> This isn't mentioned anywhere else...
>
>>  #  undef __rtld_local_attribute__
>>  # endif
>>  extern struct rtld_global _rtld_global __rtld_global_attribute__;
>> +extern char __libc_single_threaded __rtld_global_attribute__;
>
> Ok.
>
>> @@ -1119,6 +1121,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);
>> +
>
> Nor is this mentioned elsewhere.

All changes to ldsodefs.h were spurious and leftovers from the previous
version which used a hidden variable.

I'm going to retest and post updated patches.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-06-30  9:24 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-24 12:59 [PATCH v2 1/2] Add the __libc_single_threaded variable Florian Weimer
2020-06-24 13:03 ` [PATCH v2 2/2] manual: Document __libc_single_threaded Florian Weimer
2020-06-25 12:08   ` Szabolcs Nagy
2020-06-30  8:56     ` Florian Weimer
2020-06-30  1:06   ` DJ Delorie
2020-06-30  8:57     ` Florian Weimer
2020-06-30  0:52 ` [PATCH v2 1/2] Add the __libc_single_threaded variable DJ Delorie
2020-06-30  9:24   ` Florian Weimer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).