From 1b96e9580714d7a677d10a8e286b3f8a2bddc6f5 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Tue, 23 Jan 2024 01:40:24 -0500 Subject: [PATCH] libcdecl: Punt gnulib tls module. It seems like the gnulib tls module doesn't actually help much. Drop it, and implement suitable shims directly in the error handling code. It does not look like gnulib performs any feature tests regarding the TLS functionality: it simply goes and uses the appropriate functions for the selected API (posix, isoc, windows or none). With the exception of destructor callbacks, all of these TLS APIs are essentially identical. Changing this doesn't fix the problem with TLS destructors on Windows but it does delete almost 2 kilobytes of disfunctional gnulib code that purports to implement TLS destructors without actually doing so. --- Makefile.am | 5 ++- bootstrap | 2 +- lib/local/modules/dx-threadlib | 1 - src/error.c | 54 ++++++++++++++++----------- src/thread-posix.h | 68 ++++++++++++++++++++++++++++++++++ src/thread-stdc.h | 38 +++++++++++++++++++ src/thread-w32.h | 39 +++++++++++++++++++ 7 files changed, 181 insertions(+), 26 deletions(-) create mode 100644 src/thread-posix.h create mode 100644 src/thread-stdc.h create mode 100644 src/thread-w32.h diff --git a/Makefile.am b/Makefile.am index 4b3ea88..f3625c8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,8 +32,9 @@ EXTRA_DIST = bootstrap $(DX_BASEDIR)/scripts/fix-gnulib.pl m4/gnulib-cache.m4 \ dist_man_MANS = doc/cdecl99.1 doc/libcdecl.3 noinst_HEADERS = conf_pre.h conf_post.h common/src/help.h common/src/tap.h \ - common/src/xtra.h src/cdecl.h src/intconv.h src/scan.h \ - src/parse.h src/version.h t/declgen.h t/test.h + common/src/xtra.h src/cdecl.h src/intconv.h src/parse.h \ + src/scan.h src/thread-posix.h src/thread-stdc.h \ + src/thread-w32.h src/version.h t/declgen.h t/test.h noinst_DATA = $(MOFILES) diff --git a/bootstrap b/bootstrap index 328bbca..3f3f2a3 100755 --- a/bootstrap +++ b/bootstrap @@ -33,7 +33,7 @@ if test -x $GNULIB/gnulib-tool; then exec 3>lib/symfiles.tmp 4&3 ) || die "gnulib-tool failed" shared_modules=`LC_ALL=C sort -u <&4` diff --git a/lib/local/modules/dx-threadlib b/lib/local/modules/dx-threadlib index 6578fdc..85699e3 100644 --- a/lib/local/modules/dx-threadlib +++ b/lib/local/modules/dx-threadlib @@ -8,7 +8,6 @@ Depends-on: gnulib-local threadlib lock [test x"$glthreads_api" != x"none"] -tls [test x"$glthreads_api" != x"none"] configure.ac-early: dnl AC_REQUIRE has the effect of making the macro expansion unconditional; diff --git a/src/error.c b/src/error.c index 94987af..0ff31ee 100644 --- a/src/error.c +++ b/src/error.c @@ -20,15 +20,30 @@ #include #include #include +#include #include "cdecl.h" #include "cdecl-internal.h" #include -#include #include "errmsg.h" +#if USE_POSIX_THREADS +# include "thread-posix.h" +#elif USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS +# include "thread-stdc.h" +#elif USE_WINDOWS_THREADS +# include "thread-w32.h" +#else +static void *tls_key; +enum { tls_key_valid = 1 }; + +#define tls_key_init() ((void)0) +#define tls_get() tls_key +#define tls_set(a) (tls_key = (a), 1) +#endif + struct err_state { struct cdecl_error err; size_t nstr; @@ -37,15 +52,6 @@ struct err_state { /* This pre-initialized error is reserved for dire out-of-memory conditions. */ static struct cdecl_error err_no_mem; -static gl_tls_key_t tls_key; - -static void free_err(void *err) -{ - if (err == &err_no_mem) - return; - - free(err); -} static void set_err(unsigned code, struct cdecl_error *err) { @@ -70,7 +76,7 @@ static void initialize_cb(void) bindtextdomain("bison-runtime", BISON_LOCALEDIR); #endif set_err(CDECL__ENOMEM, &err_no_mem); - gl_tls_key_init(tls_key, free_err); + tls_key_init(); } static void *alloc_err_state(void *old, size_t buf_size) @@ -81,16 +87,22 @@ static void *alloc_err_state(void *old, size_t buf_size) state = p = realloc(old, offsetof(struct err_state, str) + buf_size); if (state) { state->nstr = buf_size; + if (!tls_set(state)) { + /* + * We have to presume that pthread_setspecific etc. + * cannot fail after the key has been successfully + * assigned once, because there seems to be no + * reasonable recovery from such a scenario. + */ + free(p); + p = NULL; + } } else if (old) { /* Failed allocation, but existing state is still good */ p = old; - } else { - /* Failed allocation, no existing state */ - p = &err_no_mem; } - gl_tls_set(tls_key, p); - return state; + return p; } static struct err_state *get_err_state(void) @@ -98,12 +110,10 @@ static struct err_state *get_err_state(void) void *state; gl_once_define(static, tls_initialized) - gl_once(tls_initialized, initialize_cb); + if (glthread_once(&tls_initialized, initialize_cb) || !tls_key_valid) + return NULL; - state = gl_tls_get(tls_key); - if (state == &err_no_mem) - state = NULL; - if (!state) + if (!(state = tls_get())) return alloc_err_state(state, 100); return state; } @@ -197,5 +207,5 @@ const struct cdecl_error *cdecl_get_error(void) { struct err_state *state = get_err_state(); - return state ? &state->err : NULL; + return state ? &state->err : &err_no_mem; } diff --git a/src/thread-posix.h b/src/thread-posix.h new file mode 100644 index 0000000..b1cd990 --- /dev/null +++ b/src/thread-posix.h @@ -0,0 +1,68 @@ +/* + * Copyright © 2024 Nick Bowler + * + * Helpers for hosts using POSIX threading (pthreads) API. + * + * 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 . + */ + +#include + +#if USE_POSIX_THREADS_WEAK +# pragma weak pthread_key_create +# pragma weak pthread_getspecific +# pragma weak pthread_setspecific +#endif + +static struct { + union { + pthread_key_t mt; +#if USE_POSIX_THREADS_WEAK + void *st; +#endif + } u; + +#if USE_POSIX_THREADS_WEAK + signed char valid; +#else + bool valid; +#endif +} tls_key; +#define tls_key_valid (tls_key.valid != 0) + +static void tls_key_init(void) +{ + if (pthread_in_use()) + tls_key.valid = !pthread_key_create(&tls_key.u.mt, free); + else + tls_key.valid = -1; +} + +static void *tls_get(void) +{ +#if USE_POSIX_THREADS_WEAK + if (tls_key.valid < 0) + return tls_key.u.st; +#endif + return pthread_getspecific(tls_key.u.mt); +} + +static int tls_set(void *p) +{ +#if USE_POSIX_THREADS_WEAK + if (tls_key.valid < 0) + return (tls_key.u.st = p, 1); +#endif + return !pthread_setspecific(tls_key.u.mt, p); +} diff --git a/src/thread-stdc.h b/src/thread-stdc.h new file mode 100644 index 0000000..46fe361 --- /dev/null +++ b/src/thread-stdc.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2024 Nick Bowler + * + * Helpers for hosts using standard C threading API. + * + * 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 . + */ + +#include + +static tss_t tls_key; +static bool tls_key_valid; + +static void tls_key_init(void) +{ + tls_key_valid = (tss_create(&tls_key, free) == thrd_success); +} + +static void *tls_get(void) +{ + return tss_get(tls_key); +} + +static int tls_set(void *p) +{ + return tss_set(tls_key, p) == thrd_success; +} diff --git a/src/thread-w32.h b/src/thread-w32.h new file mode 100644 index 0000000..44282d8 --- /dev/null +++ b/src/thread-w32.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2024 Nick Bowler + * + * Helpers for hosts using native Windows threading API. + * + * 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 . + */ + +#define WIN32_LEAN_AND_MEAN +#include + +static DWORD tls_key; +#define tls_key_valid (tls_key != TLS_OUT_OF_INDEXES) + +static void tls_key_init(void) +{ + tls_key = TlsAlloc(); +} + +static void *tls_get(void) +{ + return TlsGetValue(tls_key); +} + +static BOOL tls_set(void *p) +{ + return TlsSetValue(tls_key, p); +} -- 2.43.2