diff options
author | Norbert Preining <norbert@preining.info> | 2025-02-08 03:04:57 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2025-02-08 03:04:57 +0000 |
commit | c396db7db4c16dc420a640316c596b97fa46fe94 (patch) | |
tree | 6da4f12e7235fab80f7b6a37d2cc4fec4082dc63 /graphics/asymptote/libatomic_ops/src/atomic_ops.c | |
parent | 34739835e3cbf8d262d3fff2edeb26697096123c (diff) |
CTAN sync 202502080304
Diffstat (limited to 'graphics/asymptote/libatomic_ops/src/atomic_ops.c')
-rw-r--r-- | graphics/asymptote/libatomic_ops/src/atomic_ops.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/graphics/asymptote/libatomic_ops/src/atomic_ops.c b/graphics/asymptote/libatomic_ops/src/atomic_ops.c new file mode 100644 index 0000000000..e1dfdb79ee --- /dev/null +++ b/graphics/asymptote/libatomic_ops/src/atomic_ops.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2003-2011 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Initialized data and out-of-line functions to support atomic_ops.h + * go here. Currently this is needed only for pthread-based atomics + * emulation, or for compare-and-swap emulation. + * Pthreads emulation isn't useful on a native Windows platform, and + * cas emulation is not needed. Thus we skip this on Windows. + */ + +#if defined(HAVE_CONFIG_H) +# include "config.h" +#endif + +#if (defined(__hexagon__) || defined(__native_client__)) \ + && !defined(AO_USE_NO_SIGNALS) && !defined(AO_USE_NANOSLEEP) + /* Hexagon QuRT does not have sigprocmask (but Hexagon does not need */ + /* emulation, so it is OK not to bother about signals blocking). */ + /* Since NaCl is not recognized by configure yet, we do it here. */ +# define AO_USE_NO_SIGNALS +# define AO_USE_NANOSLEEP +#endif + +#if defined(AO_USE_WIN32_PTHREADS) && !defined(AO_USE_NO_SIGNALS) +# define AO_USE_NO_SIGNALS +#endif + +#if (defined(__CYGWIN__) || defined(__GLIBC__) || defined(__GNU__) \ + || defined(__linux__)) \ + && !defined(AO_USE_NO_SIGNALS) && !defined(_GNU_SOURCE) +# define _GNU_SOURCE 1 +#endif + +#ifndef AO_BUILD +# define AO_BUILD +#endif + +#undef AO_REQUIRE_CAS +#include "atomic_ops.h" /* Without cas emulation! */ + +#ifdef __cplusplus + extern "C" { +#endif + +AO_API void AO_pause(int); /* defined below */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__BORLANDC__) \ + || defined(AO_USE_NO_SIGNALS) + +#ifndef AO_NO_PTHREADS +# include <pthread.h> +#endif + +#ifndef AO_USE_NO_SIGNALS +# include <signal.h> +#endif + +#ifdef AO_USE_NANOSLEEP + /* This requires _POSIX_TIMERS feature. */ +# include <sys/time.h> +# include <time.h> +#elif defined(AO_USE_WIN32_PTHREADS) +# include <windows.h> /* for Sleep() */ +#elif defined(_HPUX_SOURCE) +# include <sys/time.h> +#else +# include <sys/select.h> +#endif + +#ifndef AO_HAVE_double_t +# include "atomic_ops/sysdeps/standard_ao_double_t.h" +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +AO_API AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, + AO_t old_val, AO_t new_val); + +AO_API int +AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2); + +AO_API void AO_store_full_emulation(volatile AO_t *addr, AO_t val); + +/* Lock for pthreads-based implementation. */ +#ifndef AO_NO_PTHREADS + AO_API pthread_mutex_t AO_pt_lock; +#endif + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#ifndef AO_NO_PTHREADS + pthread_mutex_t AO_pt_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +/* + * Out of line compare-and-swap emulation based on test and set. + * + * We use a small table of locks for different compare_and_swap locations. + * Before we update perform a compare-and-swap, we grab the corresponding + * lock. Different locations may hash to the same lock, but since we + * never acquire more than one lock at a time, this can't deadlock. + * We explicitly disable signals while we perform this operation. + * + * TODO: Probably also support emulation based on Lamport + * locks, since we may not have test_and_set either. + */ +#define AO_HASH_SIZE 16 + +#define AO_HASH(x) (((unsigned long)(x) >> 12) & (AO_HASH_SIZE-1)) + +static AO_TS_t AO_locks[AO_HASH_SIZE] = { + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, + AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, AO_TS_INITIALIZER, +}; + +static void lock_ool(volatile AO_TS_t *l) +{ + int i = 0; + + while (AO_test_and_set_acquire(l) == AO_TS_SET) + AO_pause(++i); +} + +AO_INLINE void lock(volatile AO_TS_t *l) +{ + if (AO_EXPECT_FALSE(AO_test_and_set_acquire(l) == AO_TS_SET)) + lock_ool(l); +} + +AO_INLINE void unlock(volatile AO_TS_t *l) +{ + AO_CLEAR(l); +} + +#ifndef AO_USE_NO_SIGNALS + static sigset_t all_sigs; + static volatile AO_t initialized = 0; + static volatile AO_TS_t init_lock = AO_TS_INITIALIZER; + + AO_INLINE void block_all_signals(sigset_t *old_sigs_ptr) + { + if (AO_EXPECT_FALSE(!AO_load_acquire(&initialized))) + { + lock(&init_lock); + if (!initialized) + sigfillset(&all_sigs); + unlock(&init_lock); + AO_store_release(&initialized, 1); + } + sigprocmask(SIG_BLOCK, &all_sigs, old_sigs_ptr); + /* Neither sigprocmask nor pthread_sigmask is 100% */ + /* guaranteed to work here. Sigprocmask is not */ + /* guaranteed be thread safe, and pthread_sigmask */ + /* is not async-signal-safe. Under linuxthreads, */ + /* sigprocmask may block some pthreads-internal */ + /* signals. So long as we do that for short periods, */ + /* we should be OK. */ + } +#endif /* !AO_USE_NO_SIGNALS */ + +AO_API AO_t AO_fetch_compare_and_swap_emulation(volatile AO_t *addr, + AO_t old_val, AO_t new_val) +{ + AO_TS_t *my_lock = AO_locks + AO_HASH(addr); + AO_t fetched_val; + +# ifndef AO_USE_NO_SIGNALS + sigset_t old_sigs; + block_all_signals(&old_sigs); +# endif + lock(my_lock); + fetched_val = *addr; + if (fetched_val == old_val) + *addr = new_val; + unlock(my_lock); +# ifndef AO_USE_NO_SIGNALS + sigprocmask(SIG_SETMASK, &old_sigs, NULL); +# endif + return fetched_val; +} + +AO_API int +AO_compare_double_and_swap_double_emulation(volatile AO_double_t *addr, + AO_t old_val1, AO_t old_val2, + AO_t new_val1, AO_t new_val2) +{ + AO_TS_t *my_lock = AO_locks + AO_HASH(addr); + int result; + +# ifndef AO_USE_NO_SIGNALS + sigset_t old_sigs; + block_all_signals(&old_sigs); +# endif + lock(my_lock); + if (addr -> AO_val1 == old_val1 && addr -> AO_val2 == old_val2) + { + addr -> AO_val1 = new_val1; + addr -> AO_val2 = new_val2; + result = 1; + } + else + result = 0; + unlock(my_lock); +# ifndef AO_USE_NO_SIGNALS + sigprocmask(SIG_SETMASK, &old_sigs, NULL); +# endif + return result; +} + +AO_API void AO_store_full_emulation(volatile AO_t *addr, AO_t val) +{ + AO_TS_t *my_lock = AO_locks + AO_HASH(addr); + lock(my_lock); + *addr = val; + unlock(my_lock); +} + +#else /* Non-posix platform */ + +# include <windows.h> + +# define AO_USE_WIN32_PTHREADS + /* define to use Sleep() */ + + extern int AO_non_posix_implementation_is_entirely_in_headers; + +#endif + +static AO_t spin_dummy = 1; + +/* Spin for 2**n units. */ +static void AO_spin(int n) +{ + AO_t j = AO_load(&spin_dummy); + int i = 2 << n; + + while (i-- > 0) + j += (j - 1) << 2; + /* Given 'spin_dummy' is initialized to 1, j is 1 after the loop. */ + AO_store(&spin_dummy, j); +} + +AO_API void AO_pause(int n) +{ + if (n < 12) + AO_spin(n); + else + { +# ifdef AO_USE_NANOSLEEP + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = n > 28 ? 100000L * 1000 : 1L << (n - 2); + nanosleep(&ts, 0); +# elif defined(AO_USE_WIN32_PTHREADS) + Sleep(n > 28 ? 100 /* millis */ + : n < 22 ? 1 : (DWORD)1 << (n - 22)); +# else + struct timeval tv; + /* Short async-signal-safe sleep. */ + int usec = n > 28 ? 100000 : 1 << (n - 12); + /* Use an intermediate variable (of int type) to avoid */ + /* "shift followed by widening conversion" warning. */ + + tv.tv_sec = 0; + tv.tv_usec = usec; + (void)select(0, 0, 0, 0, &tv); +# endif + } +} |