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