From c267ef7306fe9fc1399833e3556cd9798dec2eb0 Mon Sep 17 00:00:00 2001 From: Mark Adler Date: Mon, 5 Jan 2026 01:15:38 -0600 Subject: Use atomics to build inflate fixed tables once. This moves the once code from crc32.c to zutil.c, and uses it also for building the inflate fixed tables when BUILDFIXED is defined. The fixed tables are now housed in inftrees.c, shared by inflate.c and infback.c. The once() function is now external, and so is renamed to z_once() to avoid name collisions. If either BUILDFIXED or DYNAMIC_CRC_TABLE is defined, and atomics are not available, then a warning is issued noting that zlib is not thread-safe. --- zutil.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'zutil.c') diff --git a/zutil.c b/zutil.c index 6e8a369..3a94e91 100644 --- a/zutil.c +++ b/zutil.c @@ -305,3 +305,61 @@ void ZLIB_INTERNAL zcfree(voidpf opaque, voidpf ptr) { #endif /* MY_ZCALLOC */ #endif /* !Z_SOLO */ + +#if defined(BUILDFIXED) || defined(DYNAMIC_CRC_TABLE) +/* + Define a z_once() function depending on the availability of atomics. + */ + +/* Check for the availability of atomics. */ +#if defined(__STDC__) && __STDC_VERSION__ >= 201112L && \ + !defined(__STDC_NO_ATOMICS__) + +#include + +/* + Run the provided init() function exactly once, even if multiple threads + invoke once() at the same time. The state must be a once_t initialized with + Z_ONCE_INIT. + */ +void z_once(z_once_t *state, void (*init)(void)) { + if (!atomic_load(&state->done)) { + if (atomic_flag_test_and_set(&state->begun)) + while (!atomic_load(&state->done)) + ; + else { + init(); + atomic_store(&state->done, 1); + } + } +} + +#else /* no atomics */ + +#warning zlib not thread-safe + +/* Test and set. Alas, not atomic, but tries to limit the period of + vulnerability. */ +local int test_and_set(int volatile *flag) { + int was; + + was = *flag; + *flag = 1; + return was; +} + +/* Run the provided init() function once. This is not thread-safe. */ +void z_once(z_once_t *state, void (*init)(void)) { + if (!state->done) { + if (test_and_set(&state->begun)) + while (!state->done) + ; + else { + init(); + state->done = 1; + } + } +} + +#endif +#endif -- cgit v1.2.3-55-g6feb