The glibc 2.32 announcement says: Using weak references to libpthread functions such as pthread_create or pthread_key_create to detect the singled-threaded nature of a program is an obsolescent feature. Future versions of glibc will define pthread_create within libc.so.6 itself, so such checks will always flag the program as multi-threaded. Applications should check the __libc_single_threaded variable declared in <sys/single_threaded.h> instead.
See also <https://www.gnu.org/software/libc/manual/html_node/Single_002dThreaded.html>. So, this means, the pthread_in_use() macro will evaluate to 1 always in some future versions of glibc. The new __libc_single_threaded variable is not a full replacement for this. In these future glibc versions, - It will not be possible to skip the initialization of locks, only the lock/unlock operations - because some part of the program may create a thread, and then __libc_single_threaded changes from 1 to 0. - Optimization of TLS (pthread_getspecific and pthread_setspecific functions) is hairy as well. Let me add support for this technique to some gnulib modules. 'regex' could use the same technique as well. May I leave this to you, Paul? 2020-08-08 Bruno Haible <[email protected]> localename: Use module 'thread-optim'. * lib/localename.c: Include thread-optim.h. (struniq): Use IF_MT macro. * modules/localename (Depends-on): Add thread-optim. clean-temp: Use module 'thread-optim'. * lib/clean-temp.c: Include thread-optim.h. (register_temporary_file, unregister_temporary_file, create_temp_dir, register_temp_file, unregister_temp_file, register_temp_subdir, unregister_temp_subdir, cleanup_temp_dir_contents, register_fd, close_temp, fclose_variant_temp): Use IF_MT macro. * modules/clean-temp (Depends-on): Add thread-optim. fatal-signal: Use module 'thread-optim'. * lib/fatal-signal.c: Include thread-optim.h. (at_fatal_signal, block_fatal_signals, unblock_fatal_signals): Use IF_MT macro. * modules/fatal-signal (Depends-on): Add thread-optim.
>From db6c51cb278392aee178a3e836f37e0f2a7b1990 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sat, 8 Aug 2020 19:47:28 +0200 Subject: [PATCH 1/4] New module 'thread-optim'. * lib/thread-optim.h: New file. * modules/thread-optim: New file. * doc/multithread.texi (Multithreading Optimizations): New section. --- ChangeLog | 7 ++++++ doc/multithread.texi | 47 ++++++++++++++++++++++++++++++++++++++++ lib/thread-optim.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ modules/thread-optim | 21 ++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 lib/thread-optim.h create mode 100644 modules/thread-optim diff --git a/ChangeLog b/ChangeLog index 73e2056..ee9b6ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2020-08-08 Bruno Haible <[email protected]> + + New module 'thread-optim'. + * lib/thread-optim.h: New file. + * modules/thread-optim: New file. + * doc/multithread.texi (Multithreading Optimizations): New section. + 2020-08-07 Paul Eggert <[email protected]> doc: more updates for glibc 2.32 diff --git a/doc/multithread.texi b/doc/multithread.texi index b600517..022a6cb 100644 --- a/doc/multithread.texi +++ b/doc/multithread.texi @@ -60,6 +60,7 @@ Nevertheless, they need to use mutexes/locks in many cases. * POSIX multithreading:: * ISO C multithreading:: * Gnulib multithreading:: +* Multithreading Optimizations:: @end menu @node Multithreading APIs @@ -216,3 +217,49 @@ native Windows platforms (mingw and MSVC). ISO C multithreading API. However, @code{--enable-threads=posix} is always a better choice. @end itemize + +@node Multithreading Optimizations +@section Optimizations of multithreaded code + +Despite all the optimizations of multithreading primitives that have been +implemented over the years --- from +@url{https://en.wikipedia.org/wiki/Compare-and-swap, +atomic operations in hardware}, +over @url{https://en.wikipedia.org/wiki/Futex, futexes} and +@url{https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/, +restartable sequences} +in the Linux kernel, to lock elision +@url{https://lwn.net/Articles/534758/, [1]} +@url{https://www.gnu.org/software/libc/manual/html_node/Elision-Tunables.html, +[2]}) +--- single-threaded programs can still profit performance-wise from the +assertion that they are single-threaded. + +Gnulib defines four facilities that help optimizing for the single-threaded +case. + +@itemize @bullet +@item +The Gnulib multithreading API, when used on glibc @leq{} 2.32 and *BSD systems, +uses weak symbols to detect whether the program is linked with +@code{libpthread}. If not, the program has no way to create additional +threads and must therefore be single-threaded. This optimization applies +to all the Gnulib multithreading API (locks, thread-local storage, and more). +@item +The @code{thread-optim} module, on glibc @geq{} 2.32 systems, allows your code +to skip locking between threads (regardless which of the three multithreading +APIs you use). You need extra code for this: include the +@code{"thread-optim.h"} header file, and use the macros @code{IF_MT_DECL} +and @code{IF_MT}. +@item +The @code{unlocked-io} module is applicable only if all the programs in your +package are single-threaded. It optimizes the operations on @code{FILE} +streams. You need extra code for this: include the @code{"unlocked-io.h"} +header file. Some Gnulib modules that do operations on @code{FILE} streams +have these preparations already included. +@item +You may define the C macro @code{GNULIB_WCHAR_SINGLE}, if all the programs in +your package are single-threaded and won't change the locale after it has +been initialized. This macro optimizes the functions @code{mbrtowc} and +@code{wcwidth}. +@end itemize diff --git a/lib/thread-optim.h b/lib/thread-optim.h new file mode 100644 index 0000000..8040d53 --- /dev/null +++ b/lib/thread-optim.h @@ -0,0 +1,60 @@ +/* Optimization of multithreaded code. + + Copyright (C) 2020 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <[email protected]>, 2020. */ + +#ifndef _THREAD_OPTIM_H +#define _THREAD_OPTIM_H + +/* This file defines a way to optimize multithreaded code for the single-thread + case, based on the variable '__libc_single_threaded', defined in + glibc >= 2.32. */ + +/* Typical use: In a block or function, use + + IF_MT_DECL; + ... + IF_MT + if (pthread_mutex_lock (&lock)) abort (); + ... + IF_MT + if (pthread_mutex_unlock (&lock)) abort (); + + The macro IF_MT_DECL establishes local variables for use by IF_MT. + + IF_MT STATEMENT executes STATEMENT in the multithreaded cases, and skips it + in the single-threaded case. + + The code between IF_MT_DECL and IF_MT must not create threads or invoke + functions that may indirectly create threads (e.g. 'dlopen' may, indirectly + through C++ initializers of global variables in the shared library being + opened, create threads). + + The lock here is meant to synchronize threads in the same process. The + same optimization cannot be applied to locks that synchronize different + processes (e.g. through shared memory mappings). */ + +#if HAVE_SYS_SINGLE_THREADED_H /* glibc >= 2.32 */ +# include <sys/single_threaded.h> +# define IF_MT_DECL char optimize_for_single_thread = __libc_single_threaded +# define IF_MT if (optimize_for_single_thread) +#else +# define IF_MT_DECL (void *)0 +# define IF_MT +#endif + +#endif /* _THREAD_OPTIM_H */ diff --git a/modules/thread-optim b/modules/thread-optim new file mode 100644 index 0000000..caf4518 --- /dev/null +++ b/modules/thread-optim @@ -0,0 +1,21 @@ +Description: +Optimization of multithreaded code. + +Files: +lib/thread-optim.h + +Depends-on: + +configure.ac: +AC_CHECK_HEADERS([sys/single_threaded.h]) + +Makefile.am: + +Include: +"thread-optim.h" + +License: +LGPLv2+ + +Maintainer: +all -- 2.7.4
>From f9f3407212edb967b97d14f007919e7ee0927135 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sat, 8 Aug 2020 22:05:25 +0200 Subject: [PATCH 2/4] fatal-signal: Use module 'thread-optim'. * lib/fatal-signal.c: Include thread-optim.h. (at_fatal_signal, block_fatal_signals, unblock_fatal_signals): Use IF_MT macro. * modules/fatal-signal (Depends-on): Add thread-optim. --- ChangeLog | 8 ++++++++ lib/fatal-signal.c | 19 +++++++++++++------ modules/fatal-signal | 1 + 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index ee9b6ce..37a7a07 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2020-08-08 Bruno Haible <[email protected]> + fatal-signal: Use module 'thread-optim'. + * lib/fatal-signal.c: Include thread-optim.h. + (at_fatal_signal, block_fatal_signals, unblock_fatal_signals): Use IF_MT + macro. + * modules/fatal-signal (Depends-on): Add thread-optim. + +2020-08-08 Bruno Haible <[email protected]> + New module 'thread-optim'. * lib/thread-optim.h: New file. * modules/thread-optim: New file. diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c index 29184e0..541c4cb 100644 --- a/lib/fatal-signal.c +++ b/lib/fatal-signal.c @@ -27,6 +27,7 @@ #include <unistd.h> #include "glthread/lock.h" +#include "thread-optim.h" #include "sig-handler.h" #include "xalloc.h" @@ -213,7 +214,9 @@ gl_lock_define_initialized (static, at_fatal_signal_lock) void at_fatal_signal (action_t action) { - gl_lock_lock (at_fatal_signal_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (at_fatal_signal_lock); static bool cleanup_initialized = false; if (!cleanup_initialized) @@ -260,7 +263,7 @@ at_fatal_signal (action_t action) actions[actions_count].action = action; actions_count++; - gl_lock_unlock (at_fatal_signal_lock); + IF_MT gl_lock_unlock (at_fatal_signal_lock); } @@ -300,7 +303,9 @@ static unsigned int fatal_signals_block_counter = 0; void block_fatal_signals (void) { - gl_lock_lock (fatal_signals_block_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (fatal_signals_block_lock); if (fatal_signals_block_counter++ == 0) { @@ -308,14 +313,16 @@ block_fatal_signals (void) sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL); } - gl_lock_unlock (fatal_signals_block_lock); + IF_MT gl_lock_unlock (fatal_signals_block_lock); } /* Stop delaying the catchable fatal signals. */ void unblock_fatal_signals (void) { - gl_lock_lock (fatal_signals_block_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (fatal_signals_block_lock); if (fatal_signals_block_counter == 0) /* There are more calls to unblock_fatal_signals() than to @@ -327,7 +334,7 @@ unblock_fatal_signals (void) sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL); } - gl_lock_unlock (fatal_signals_block_lock); + IF_MT gl_lock_unlock (fatal_signals_block_lock); } diff --git a/modules/fatal-signal b/modules/fatal-signal index de53de7..7029687 100644 --- a/modules/fatal-signal +++ b/modules/fatal-signal @@ -13,6 +13,7 @@ stdbool unistd sigaction lock +thread-optim sigprocmask raise -- 2.7.4
>From a7c4d409435d09428d990c4d4adeea09c4c00912 Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sat, 8 Aug 2020 22:09:09 +0200 Subject: [PATCH 3/4] clean-temp: Use module 'thread-optim'. * lib/clean-temp.c: Include thread-optim.h. (register_temporary_file, unregister_temporary_file, create_temp_dir, register_temp_file, unregister_temp_file, register_temp_subdir, unregister_temp_subdir, cleanup_temp_dir_contents, register_fd, close_temp, fclose_variant_temp): Use IF_MT macro. * modules/clean-temp (Depends-on): Add thread-optim. --- ChangeLog | 8 +++++++ lib/clean-temp.c | 65 +++++++++++++++++++++++++++++++++++------------------- modules/clean-temp | 1 + 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index 37a7a07..1ee85d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2020-08-08 Bruno Haible <[email protected]> + clean-temp: Use module 'thread-optim'. + * lib/clean-temp.c: Include thread-optim.h. + (register_temporary_file, unregister_temporary_file, create_temp_dir, + register_temp_file, unregister_temp_file, register_temp_subdir, + unregister_temp_subdir, cleanup_temp_dir_contents, register_fd, + close_temp, fclose_variant_temp): Use IF_MT macro. + * modules/clean-temp (Depends-on): Add thread-optim. + fatal-signal: Use module 'thread-optim'. * lib/fatal-signal.c: Include thread-optim.h. (at_fatal_signal, block_fatal_signals, unblock_fatal_signals): Use IF_MT diff --git a/lib/clean-temp.c b/lib/clean-temp.c index 34ebb9b..605390b 100644 --- a/lib/clean-temp.c +++ b/lib/clean-temp.c @@ -44,6 +44,7 @@ #include "xalloc.h" #include "xmalloca.h" #include "glthread/lock.h" +#include "thread-optim.h" #include "gl_xlist.h" #include "gl_linkedhash_list.h" #include "gl_linked_list.h" @@ -406,7 +407,9 @@ init_clean_temp (void) void register_temporary_file (const char *absolute_file_name) { - gl_lock_lock (file_cleanup_list_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (file_cleanup_list_lock); /* Make sure that this facility and the file_cleanup_list are initialized. */ if (file_cleanup_list == NULL) @@ -421,7 +424,7 @@ register_temporary_file (const char *absolute_file_name) if (gl_list_search (file_cleanup_list, absolute_file_name) == NULL) gl_list_add_first (file_cleanup_list, xstrdup (absolute_file_name)); - gl_lock_unlock (file_cleanup_list_lock); + IF_MT gl_lock_unlock (file_cleanup_list_lock); } /* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be @@ -430,7 +433,9 @@ register_temporary_file (const char *absolute_file_name) void unregister_temporary_file (const char *absolute_file_name) { - gl_lock_lock (file_cleanup_list_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (file_cleanup_list_lock); gl_list_t list = file_cleanup_list; if (list != NULL) @@ -445,7 +450,7 @@ unregister_temporary_file (const char *absolute_file_name) } } - gl_lock_unlock (file_cleanup_list_lock); + IF_MT gl_lock_unlock (file_cleanup_list_lock); } /* Remove a file, with optional error message. @@ -493,7 +498,9 @@ struct temp_dir * create_temp_dir (const char *prefix, const char *parentdir, bool cleanup_verbose) { - gl_lock_lock (dir_cleanup_list_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (dir_cleanup_list_lock); struct tempdir * volatile *tmpdirp = NULL; struct tempdir *tmpdir; @@ -600,12 +607,12 @@ create_temp_dir (const char *prefix, const char *parentdir, block because then the cleanup handler would not remove the directory if xstrdup fails. */ tmpdir->dirname = xstrdup (tmpdirname); - gl_lock_unlock (dir_cleanup_list_lock); + IF_MT gl_lock_unlock (dir_cleanup_list_lock); freea (xtemplate); return (struct temp_dir *) tmpdir; quit: - gl_lock_unlock (dir_cleanup_list_lock); + IF_MT gl_lock_unlock (dir_cleanup_list_lock); freea (xtemplate); return NULL; } @@ -618,14 +625,15 @@ register_temp_file (struct temp_dir *dir, const char *absolute_file_name) { struct tempdir *tmpdir = (struct tempdir *)dir; + IF_MT_DECL; - gl_lock_lock (dir_cleanup_list_lock); + IF_MT gl_lock_lock (dir_cleanup_list_lock); /* Add absolute_file_name to tmpdir->files, without duplicates. */ if (gl_list_search (tmpdir->files, absolute_file_name) == NULL) gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name)); - gl_lock_unlock (dir_cleanup_list_lock); + IF_MT gl_lock_unlock (dir_cleanup_list_lock); } /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that @@ -636,8 +644,9 @@ unregister_temp_file (struct temp_dir *dir, const char *absolute_file_name) { struct tempdir *tmpdir = (struct tempdir *)dir; + IF_MT_DECL; - gl_lock_lock (dir_cleanup_list_lock); + IF_MT gl_lock_lock (dir_cleanup_list_lock); gl_list_t list = tmpdir->files; gl_list_node_t node; @@ -651,7 +660,7 @@ unregister_temp_file (struct temp_dir *dir, free (old_string); } - gl_lock_unlock (dir_cleanup_list_lock); + IF_MT gl_lock_unlock (dir_cleanup_list_lock); } /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, @@ -662,14 +671,15 @@ register_temp_subdir (struct temp_dir *dir, const char *absolute_dir_name) { struct tempdir *tmpdir = (struct tempdir *)dir; + IF_MT_DECL; - gl_lock_lock (dir_cleanup_list_lock); + IF_MT gl_lock_lock (dir_cleanup_list_lock); /* Add absolute_dir_name to tmpdir->subdirs, without duplicates. */ if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL) gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name)); - gl_lock_unlock (dir_cleanup_list_lock); + IF_MT gl_lock_unlock (dir_cleanup_list_lock); } /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, @@ -681,8 +691,9 @@ unregister_temp_subdir (struct temp_dir *dir, const char *absolute_dir_name) { struct tempdir *tmpdir = (struct tempdir *)dir; + IF_MT_DECL; - gl_lock_lock (dir_cleanup_list_lock); + IF_MT gl_lock_lock (dir_cleanup_list_lock); gl_list_t list = tmpdir->subdirs; gl_list_node_t node; @@ -696,7 +707,7 @@ unregister_temp_subdir (struct temp_dir *dir, free (old_string); } - gl_lock_unlock (dir_cleanup_list_lock); + IF_MT gl_lock_unlock (dir_cleanup_list_lock); } /* Remove a directory, with optional error message. @@ -792,7 +803,9 @@ cleanup_temp_dir_contents (struct temp_dir *dir) int cleanup_temp_dir (struct temp_dir *dir) { - gl_lock_lock (dir_cleanup_list_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (dir_cleanup_list_lock); struct tempdir *tmpdir = (struct tempdir *)dir; int err = 0; @@ -819,7 +832,7 @@ cleanup_temp_dir (struct temp_dir *dir) gl_list_free (tmpdir->subdirs); free (tmpdir->dirname); free (tmpdir); - gl_lock_unlock (dir_cleanup_list_lock); + IF_MT gl_lock_unlock (dir_cleanup_list_lock); return err; } @@ -866,7 +879,9 @@ supports_delete_on_close () static void register_fd (int fd) { - gl_lock_lock (descriptors_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (descriptors_lock); if (descriptors == NULL) descriptors = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL, @@ -880,7 +895,7 @@ register_fd (int fd) gl_list_add_first (descriptors, element); - gl_lock_unlock (descriptors_lock); + IF_MT gl_lock_unlock (descriptors_lock); } /* Open a temporary file in a temporary directory. @@ -1026,7 +1041,9 @@ close_temp (int fd) int result = 0; int saved_errno = 0; - gl_lock_lock (descriptors_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (descriptors_lock); gl_list_t list = descriptors; if (list == NULL) @@ -1072,7 +1089,7 @@ close_temp (int fd) /* descriptors should already contain fd. */ abort (); - gl_lock_unlock (descriptors_lock); + IF_MT gl_lock_unlock (descriptors_lock); errno = saved_errno; return result; @@ -1088,7 +1105,9 @@ fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *)) int result = 0; int saved_errno = 0; - gl_lock_lock (descriptors_lock); + IF_MT_DECL; + + IF_MT gl_lock_lock (descriptors_lock); gl_list_t list = descriptors; if (list == NULL) @@ -1134,7 +1153,7 @@ fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *)) /* descriptors should have contained fd. */ abort (); - gl_lock_unlock (descriptors_lock); + IF_MT gl_lock_unlock (descriptors_lock); errno = saved_errno; return result; diff --git a/modules/clean-temp b/modules/clean-temp index 940869d..69157a5 100644 --- a/modules/clean-temp +++ b/modules/clean-temp @@ -10,6 +10,7 @@ stdbool stdint unistd lock +thread-optim error fatal-signal asyncsafe-spin -- 2.7.4
>From 50ccda39b28fe3c8bc4de61e36184ddbf0f3e3cd Mon Sep 17 00:00:00 2001 From: Bruno Haible <[email protected]> Date: Sat, 8 Aug 2020 22:11:50 +0200 Subject: [PATCH 4/4] localename: Use module 'thread-optim'. * lib/localename.c: Include thread-optim.h. (struniq): Use IF_MT macro. * modules/localename (Depends-on): Add thread-optim. --- ChangeLog | 5 +++++ lib/localename.c | 40 ++++++++++++++++++++++------------------ modules/localename | 1 + 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1ee85d6..d9a78c4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2020-08-08 Bruno Haible <[email protected]> + localename: Use module 'thread-optim'. + * lib/localename.c: Include thread-optim.h. + (struniq): Use IF_MT macro. + * modules/localename (Depends-on): Add thread-optim. + clean-temp: Use module 'thread-optim'. * lib/clean-temp.c: Include thread-optim.h. (register_temporary_file, unregister_temporary_file, create_temp_dir, diff --git a/lib/localename.c b/lib/localename.c index dc60b07..2e76c11 100644 --- a/lib/localename.c +++ b/lib/localename.c @@ -35,6 +35,7 @@ #include "flexmember.h" #include "setlocale_null.h" +#include "thread-optim.h" /* We cannot support uselocale() on platforms where the locale_t type is fake. See intl-thread-locale.m4 for details. */ @@ -2697,24 +2698,27 @@ struniq (const char *string) /* Out of memory. Return a statically allocated string. */ return "C"; memcpy (new_node->contents, string, size); - /* Lock while inserting new_node. */ - gl_lock_lock (struniq_lock); - /* Check whether another thread already added the string while we were - waiting on the lock. */ - for (p = struniq_hash_table[slot]; p != NULL; p = p->next) - if (strcmp (p->contents, string) == 0) - { - free (new_node); - new_node = p; - goto done; - } - /* Really insert new_node into the hash table. Fill new_node entirely first, - because other threads may be iterating over the linked list. */ - new_node->next = struniq_hash_table[slot]; - struniq_hash_table[slot] = new_node; - done: - /* Unlock after new_node is inserted. */ - gl_lock_unlock (struniq_lock); + { + IF_MT_DECL; + /* Lock while inserting new_node. */ + IF_MT gl_lock_lock (struniq_lock); + /* Check whether another thread already added the string while we were + waiting on the lock. */ + for (p = struniq_hash_table[slot]; p != NULL; p = p->next) + if (strcmp (p->contents, string) == 0) + { + free (new_node); + new_node = p; + goto done; + } + /* Really insert new_node into the hash table. Fill new_node entirely + first, because other threads may be iterating over the linked list. */ + new_node->next = struniq_hash_table[slot]; + struniq_hash_table[slot] = new_node; + done: + /* Unlock after new_node is inserted. */ + IF_MT gl_lock_unlock (struniq_lock); + } return new_node->contents; } diff --git a/modules/localename b/modules/localename index c528acb..28710d0 100644 --- a/modules/localename +++ b/modules/localename @@ -19,6 +19,7 @@ strdup lock langinfo setlocale-null +thread-optim configure.ac: gl_LOCALENAME -- 2.7.4
