This is a new implementation of GSCOPE which largely mirrors its NPTL counterpart. Same as in NPTL, instead of a global flag shared between threads, there is now a per-thread GSCOPE flag stored in each thread's TCB. This makes entering and exiting a GSCOPE faster at the expense of making THREAD_GSCOPE_WAIT () slower.
The largest win is the elimination of many redundant gsync_wake () RPC calls; previously, even simplest programs would make dozens of fully redundant gsync_wake () calls. Despite all ports now putting GSCOPE flag into TCP, this patch keeps the THREAD_GSCOPE_IN_TCB preprocessor definition, with value 0 for HTL and 1 for NPTL. It turns out that although originally this definition was indeed used to distinguish between the cases where the GSCOPE flag was stored in TCB or not, it has since become used as a general way to distinguish between HTL and NPTL. It may be worthwhile to get rid of this flag, or at least rename it to better reflect what it's used for. This patch does neither, though. Signed-off-by: Sergey Bugaev <buga...@gmail.com> --- elf/dl-support.c | 1 - sysdeps/generic/ldsodefs.h | 4 --- sysdeps/htl/dl-thread_gscope_wait.c | 55 +++++++++++++++++++++++++++++ sysdeps/mach/hurd/i386/tls.h | 19 ++++++++++ sysdeps/mach/hurd/tls.h | 20 +---------- 5 files changed, 75 insertions(+), 24 deletions(-) create mode 100644 sysdeps/htl/dl-thread_gscope_wait.c diff --git a/elf/dl-support.c b/elf/dl-support.c index 0155718175..68abf606ca 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -195,7 +195,6 @@ int _dl_stack_cache_lock; when it was not, we do it by calling this function. It returns an errno code or zero on success. */ int (*_dl_make_stack_executable_hook) (void **) = _dl_make_stack_executable; -int _dl_thread_gscope_count; void (*_dl_init_static_tls) (struct link_map *) = &_dl_nothread_init_static_tls; #endif struct dl_scope_free_list *_dl_scope_free_list; diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 31c7d3945b..5a779ae1a3 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -487,8 +487,6 @@ struct rtld_global /* Mutex protecting the stack lists. */ EXTERN int _dl_stack_cache_lock; #else - EXTERN int _dl_thread_gscope_count; - /* The total number of thread IDs currently in use, or on the list of available thread IDs. */ EXTERN int _dl_pthread_num_threads; @@ -1379,10 +1377,8 @@ __rtld_mutex_init (void) } #endif /* !PTHREAD_IN_LIBC */ -#if THREAD_GSCOPE_IN_TCB void __thread_gscope_wait (void) attribute_hidden; # define THREAD_GSCOPE_WAIT() __thread_gscope_wait () -#endif __END_DECLS diff --git a/sysdeps/htl/dl-thread_gscope_wait.c b/sysdeps/htl/dl-thread_gscope_wait.c new file mode 100644 index 0000000000..b277217b8e --- /dev/null +++ b/sysdeps/htl/dl-thread_gscope_wait.c @@ -0,0 +1,55 @@ +/* Out-of-line notification function for the GSCOPE locking mechanism. + Copyright (C) 2007-2021 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 + <https://www.gnu.org/licenses/>. */ + +#include <ldsodefs.h> +#include <pthread.h> +#include <htl/pt-internal.h> + +void +__thread_gscope_wait (void) +{ + size_t i; + struct __pthread *t; + int *gscope_flagp; + + lll_lock (GL (dl_pthread_threads_lock), LLL_PRIVATE); + + /* Iterate over the list of threads. */ + for (i = 0; i < GL (dl_pthread_num_threads); ++i) + { + t = GL (dl_pthread_threads[i]); + if (t == NULL || t->tcb->gscope_flag == THREAD_GSCOPE_FLAG_UNUSED) + continue; + + gscope_flagp = &t->tcb->gscope_flag; + + /* We have to wait until this thread is done with the global + scope. First tell the thread that we are waiting and + possibly have to be woken. */ + if (atomic_compare_and_exchange_bool_acq (gscope_flagp, + THREAD_GSCOPE_FLAG_WAIT, + THREAD_GSCOPE_FLAG_USED)) + continue; + + do + lll_wait (gscope_flagp, THREAD_GSCOPE_FLAG_WAIT, LLL_PRIVATE); + while (*gscope_flagp == THREAD_GSCOPE_FLAG_WAIT); + } + + lll_unlock (GL (dl_pthread_threads_lock), LLL_PRIVATE); +} diff --git a/sysdeps/mach/hurd/i386/tls.h b/sysdeps/mach/hurd/i386/tls.h index 057b2613f3..c70ea73a81 100644 --- a/sysdeps/mach/hurd/i386/tls.h +++ b/sysdeps/mach/hurd/i386/tls.h @@ -369,6 +369,25 @@ _hurd_tls_new (thread_t child, struct i386_thread_state *state, tcbhead_t *tcb) return err; } +/* Global scope switch support. */ +# define THREAD_GSCOPE_FLAG_UNUSED 0 +# define THREAD_GSCOPE_FLAG_USED 1 +# define THREAD_GSCOPE_FLAG_WAIT 2 + +# define THREAD_GSCOPE_SET_FLAG() \ + THREAD_SETMEM (THREAD_SELF, gscope_flag, THREAD_GSCOPE_FLAG_USED) + +# define THREAD_GSCOPE_RESET_FLAG() \ + ({ \ + int __flag; \ + asm volatile ("xchgl %0, %%gs:%P1" \ + : "=r" (__flag) \ + : "i" (offsetof (tcbhead_t, gscope_flag)), \ + "0" (THREAD_GSCOPE_FLAG_UNUSED)); \ + if (__flag == THREAD_GSCOPE_FLAG_WAIT) \ + lll_wake (THREAD_SELF->gscope_flag, LLL_PRIVATE); \ + }) + #endif /* !__ASSEMBLER__ */ #endif /* i386/tls.h */ diff --git a/sysdeps/mach/hurd/tls.h b/sysdeps/mach/hurd/tls.h index f83956d3d7..98dc319745 100644 --- a/sysdeps/mach/hurd/tls.h +++ b/sysdeps/mach/hurd/tls.h @@ -52,25 +52,7 @@ # define GET_DTV(descr) \ (((tcbhead_t *) (descr))->dtv) -/* Global scope switch support. */ -#define THREAD_GSCOPE_IN_TCB 0 -#define THREAD_GSCOPE_GLOBAL -#define THREAD_GSCOPE_SET_FLAG() \ - atomic_exchange_and_add_acq (&GL(dl_thread_gscope_count), 1) -#define THREAD_GSCOPE_RESET_FLAG() \ - do \ - if (atomic_exchange_and_add_rel (&GL(dl_thread_gscope_count), -1) == 1) \ - lll_wake (GL(dl_thread_gscope_count), 0); \ - while (0) -#define THREAD_GSCOPE_WAIT() \ - do \ - { \ - int count; \ - atomic_write_barrier (); \ - while ((count = GL(dl_thread_gscope_count))) \ - lll_wait (GL(dl_thread_gscope_count), count, 0); \ - } \ - while (0) +# define THREAD_GSCOPE_IN_TCB 0 #endif /* !ASSEMBLER */ -- 2.31.1