Buckets:
| /* ---------------------------------------------------------------------------- | |
| Copyright (c) 2018-2024 Microsoft Research, Daan Leijen | |
| This is free software; you can redistribute it and/or modify it under the | |
| terms of the MIT license. A copy of the license can be found in the file | |
| "LICENSE" at the root of this distribution. | |
| -----------------------------------------------------------------------------*/ | |
| // include windows.h or pthreads.h | |
| // -------------------------------------------------------------------------------------------- | |
| // Atomics | |
| // We need to be portable between C, C++, and MSVC. | |
| // We base the primitives on the C/C++ atomics and create a minimal wrapper for MSVC in C compilation mode. | |
| // This is why we try to use only `uintptr_t` and `<type>*` as atomic types. | |
| // To gain better insight in the range of used atomics, we use explicitly named memory order operations | |
| // instead of passing the memory order as a parameter. | |
| // ----------------------------------------------------------------------------------------------- | |
| // Use C++ atomics | |
| // Use MSVC C wrapper for C11 atomics | |
| // Use C11 atomics | |
| // Various defines for all used memory orders in mimalloc | |
| static inline intptr_t mi_atomic_addi(_Atomic(intptr_t)*p, intptr_t add); | |
| static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub); | |
| // In C++/C11 atomics we have polymorphic atomics so can use the typed `ptr` variants (where `tp` is the type of atomic value) | |
| // We use these macros so we can provide a typed wrapper in MSVC in C compilation mode as well | |
| // In C++ we need to add casts to help resolve templates if NULL is passed | |
| // These are used by the statistics | |
| static inline int64_t mi_atomic_addi64_relaxed(volatile int64_t* p, int64_t add) { | |
| return mi_atomic(fetch_add_explicit)((_Atomic(int64_t)*)p, add, mi_memory_order(relaxed)); | |
| } | |
| static inline void mi_atomic_void_addi64_relaxed(volatile int64_t* p, const volatile int64_t* padd) { | |
| const int64_t add = mi_atomic_load_relaxed((_Atomic(int64_t)*)padd); | |
| if (add != 0) { | |
| mi_atomic(fetch_add_explicit)((_Atomic(int64_t)*)p, add, mi_memory_order(relaxed)); | |
| } | |
| } | |
| static inline void mi_atomic_maxi64_relaxed(volatile int64_t* p, int64_t x) { | |
| int64_t current = mi_atomic_load_relaxed((_Atomic(int64_t)*)p); | |
| while (current < x && !mi_atomic_cas_weak_release((_Atomic(int64_t)*)p, ¤t, x)) { /* nothing */ }; | |
| } | |
| // Used by timers | |
| // Deprecated: MSVC plain C compilation wrapper that uses Interlocked operations to model C11 atomics. | |
| // It is recommended to always compile as C++ when using MSVC | |
| typedef LONG64 msc_intptr_t; | |
| typedef LONG msc_intptr_t; | |
| typedef enum mi_memory_order_e { | |
| mi_memory_order_relaxed, | |
| mi_memory_order_consume, | |
| mi_memory_order_acquire, | |
| mi_memory_order_release, | |
| mi_memory_order_acq_rel, | |
| mi_memory_order_seq_cst | |
| } mi_memory_order; | |
| static inline uintptr_t mi_atomic_fetch_add_explicit(_Atomic(uintptr_t)*p, uintptr_t add, mi_memory_order mo) { | |
| (void)(mo); | |
| return (uintptr_t)MI_64(_InterlockedExchangeAdd)((volatile msc_intptr_t*)p, (msc_intptr_t)add); | |
| } | |
| static inline uintptr_t mi_atomic_fetch_sub_explicit(_Atomic(uintptr_t)*p, uintptr_t sub, mi_memory_order mo) { | |
| (void)(mo); | |
| return (uintptr_t)MI_64(_InterlockedExchangeAdd)((volatile msc_intptr_t*)p, -((msc_intptr_t)sub)); | |
| } | |
| static inline uintptr_t mi_atomic_fetch_and_explicit(_Atomic(uintptr_t)*p, uintptr_t x, mi_memory_order mo) { | |
| (void)(mo); | |
| return (uintptr_t)MI_64(_InterlockedAnd)((volatile msc_intptr_t*)p, (msc_intptr_t)x); | |
| } | |
| static inline uintptr_t mi_atomic_fetch_or_explicit(_Atomic(uintptr_t)*p, uintptr_t x, mi_memory_order mo) { | |
| (void)(mo); | |
| return (uintptr_t)MI_64(_InterlockedOr)((volatile msc_intptr_t*)p, (msc_intptr_t)x); | |
| } | |
| static inline bool mi_atomic_compare_exchange_strong_explicit(_Atomic(uintptr_t)*p, uintptr_t* expected, uintptr_t desired, mi_memory_order mo1, mi_memory_order mo2) { | |
| (void)(mo1); (void)(mo2); | |
| uintptr_t read = (uintptr_t)MI_64(_InterlockedCompareExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)desired, (msc_intptr_t)(*expected)); | |
| if (read == *expected) { | |
| return true; | |
| } | |
| else { | |
| *expected = read; | |
| return false; | |
| } | |
| } | |
| static inline bool mi_atomic_compare_exchange_weak_explicit(_Atomic(uintptr_t)*p, uintptr_t* expected, uintptr_t desired, mi_memory_order mo1, mi_memory_order mo2) { | |
| return mi_atomic_compare_exchange_strong_explicit(p, expected, desired, mo1, mo2); | |
| } | |
| static inline uintptr_t mi_atomic_exchange_explicit(_Atomic(uintptr_t)*p, uintptr_t exchange, mi_memory_order mo) { | |
| (void)(mo); | |
| return (uintptr_t)MI_64(_InterlockedExchange)((volatile msc_intptr_t*)p, (msc_intptr_t)exchange); | |
| } | |
| static inline void mi_atomic_thread_fence(mi_memory_order mo) { | |
| (void)(mo); | |
| _Atomic(uintptr_t) x = 0; | |
| mi_atomic_exchange_explicit(&x, 1, mo); | |
| } | |
| static inline uintptr_t mi_atomic_load_explicit(_Atomic(uintptr_t) const* p, mi_memory_order mo) { | |
| (void)(mo); | |
| return *p; | |
| uintptr_t x = *p; | |
| if (mo > mi_memory_order_relaxed) { | |
| while (!mi_atomic_compare_exchange_weak_explicit((_Atomic(uintptr_t)*)p, &x, x, mo, mi_memory_order_relaxed)) { /* nothing */ }; | |
| } | |
| return x; | |
| } | |
| static inline void mi_atomic_store_explicit(_Atomic(uintptr_t)*p, uintptr_t x, mi_memory_order mo) { | |
| (void)(mo); | |
| *p = x; | |
| mi_atomic_exchange_explicit(p, x, mo); | |
| } | |
| static inline int64_t mi_atomic_loadi64_explicit(_Atomic(int64_t)*p, mi_memory_order mo) { | |
| (void)(mo); | |
| return *p; | |
| int64_t old = *p; | |
| int64_t x = old; | |
| while ((old = InterlockedCompareExchange64(p, x, old)) != x) { | |
| x = old; | |
| } | |
| return x; | |
| } | |
| static inline void mi_atomic_storei64_explicit(_Atomic(int64_t)*p, int64_t x, mi_memory_order mo) { | |
| (void)(mo); | |
| *p = x; | |
| InterlockedExchange64(p, x); | |
| } | |
| // These are used by the statistics | |
| static inline int64_t mi_atomic_addi64_relaxed(volatile _Atomic(int64_t)*p, int64_t add) { | |
| return (int64_t)mi_atomic_addi((int64_t*)p, add); | |
| int64_t current; | |
| int64_t sum; | |
| do { | |
| current = *p; | |
| sum = current + add; | |
| } while (_InterlockedCompareExchange64(p, sum, current) != current); | |
| return current; | |
| } | |
| static inline void mi_atomic_void_addi64_relaxed(volatile int64_t* p, const volatile int64_t* padd) { | |
| const int64_t add = *padd; | |
| if (add != 0) { | |
| mi_atomic_addi64_relaxed((volatile _Atomic(int64_t)*)p, add); | |
| } | |
| } | |
| static inline void mi_atomic_maxi64_relaxed(volatile _Atomic(int64_t)*p, int64_t x) { | |
| int64_t current; | |
| do { | |
| current = *p; | |
| } while (current < x && _InterlockedCompareExchange64(p, x, current) != current); | |
| } | |
| static inline void mi_atomic_addi64_acq_rel(volatile _Atomic(int64_t*)p, int64_t i) { | |
| mi_atomic_addi64_relaxed(p, i); | |
| } | |
| static inline bool mi_atomic_casi64_strong_acq_rel(volatile _Atomic(int64_t*)p, int64_t* exp, int64_t des) { | |
| int64_t read = _InterlockedCompareExchange64(p, des, *exp); | |
| if (read == *exp) { | |
| return true; | |
| } | |
| else { | |
| *exp = read; | |
| return false; | |
| } | |
| } | |
| // The pointer macros cast to `uintptr_t`. | |
| // Atomically add a signed value; returns the previous value. | |
| static inline intptr_t mi_atomic_addi(_Atomic(intptr_t)*p, intptr_t add) { | |
| return (intptr_t)mi_atomic_add_acq_rel((_Atomic(uintptr_t)*)p, (uintptr_t)add); | |
| } | |
| // Atomically subtract a signed value; returns the previous value. | |
| static inline intptr_t mi_atomic_subi(_Atomic(intptr_t)*p, intptr_t sub) { | |
| return (intptr_t)mi_atomic_addi(p, -sub); | |
| } | |
| // ---------------------------------------------------------------------- | |
| // Guard | |
| // ---------------------------------------------------------------------- | |
| typedef _Atomic(uintptr_t) mi_atomic_guard_t; | |
| // Allows only one thread to execute at a time (without blocking anyone) | |
| // ---------------------------------------------------------------------- | |
| // Locks | |
| // These should be light-weight in-process only locks. | |
| // Only used for reserving arena's and to maintain the abandoned list. | |
| // ---------------------------------------------------------------------- | |
| typedef struct mi_lock_s { | |
| SRWLOCK mutex; // slim reader-writer lock | |
| } mi_lock_t; | |
| static inline bool mi_lock_try_acquire(mi_lock_t* lock) { | |
| return TryAcquireSRWLockExclusive(&lock->mutex); | |
| } | |
| static inline void mi_lock_acquire(mi_lock_t* lock) { | |
| AcquireSRWLockExclusive(&lock->mutex); | |
| } | |
| static inline void mi_lock_release(mi_lock_t* lock) { | |
| ReleaseSRWLockExclusive(&lock->mutex); | |
| } | |
| static inline void mi_lock_init(mi_lock_t* lock) { | |
| InitializeSRWLock(&lock->mutex); | |
| } | |
| static inline void mi_lock_done(mi_lock_t* lock) { | |
| (void)(lock); | |
| } | |
| void _mi_error_message(int err, const char* fmt, ...); | |
| typedef struct mi_lock_s { | |
| pthread_mutex_t mutex; | |
| } mi_lock_t; | |
| static inline bool mi_lock_try_acquire(mi_lock_t* lock) { | |
| return (pthread_mutex_trylock(&lock->mutex) == 0); | |
| } | |
| static inline void mi_lock_acquire(mi_lock_t* lock) { | |
| const int err = pthread_mutex_lock(&lock->mutex); | |
| if (err != 0) { | |
| _mi_error_message(err, "internal error: lock cannot be acquired (err %i)\n", err); | |
| } | |
| } | |
| static inline void mi_lock_release(mi_lock_t* lock) { | |
| pthread_mutex_unlock(&lock->mutex); | |
| } | |
| static inline void mi_lock_init(mi_lock_t* lock) { | |
| if(lock==NULL) return; | |
| // use this instead of pthread_mutex_init since that can cause allocation on some platforms (and recursively initialize) | |
| const pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; | |
| memcpy(&lock->mutex,&mutex,sizeof(mutex)); | |
| } | |
| static inline void mi_lock_done(mi_lock_t* lock) { | |
| pthread_mutex_destroy(&lock->mutex); | |
| } | |
| typedef struct mi_lock_s { | |
| std::mutex mutex; | |
| } mi_lock_t; | |
| static inline bool mi_lock_try_acquire(mi_lock_t* lock) { | |
| return lock->mutex.try_lock(); | |
| } | |
| static inline void mi_lock_acquire(mi_lock_t* lock) { | |
| lock->mutex.lock(); | |
| } | |
| static inline void mi_lock_release(mi_lock_t* lock) { | |
| lock->mutex.unlock(); | |
| } | |
| static inline void mi_lock_init(mi_lock_t* lock) { | |
| new(&lock->mutex) std::mutex(); | |
| } | |
| static inline void mi_lock_done(mi_lock_t* lock) { | |
| (void)(lock); | |
| } | |
| // fall back to poor man's locks. | |
| // this should only be the case in a single-threaded environment (like __wasi__) | |
| void _mi_error_message(int err, const char* fmt, ...); | |
| void _mi_prim_thread_yield(void); | |
| typedef struct mi_lock_s { | |
| _Atomic(uintptr_t) mutex; | |
| } mi_lock_t; | |
| static inline bool mi_lock_try_acquire(mi_lock_t* lock) { | |
| uintptr_t expected = 0; | |
| return mi_atomic_cas_strong_acq_rel(&lock->mutex, &expected, (uintptr_t)1); | |
| } | |
| static inline void mi_lock_acquire(mi_lock_t* lock) { | |
| for (int i = 0; i < 10000; i++) { // for at most 10000 tries? | |
| if (mi_lock_try_acquire(lock)) return; | |
| _mi_prim_thread_yield(); | |
| } | |
| _mi_error_message(EFAULT, "internal error: lock cannot be acquired (due to lack of native lock primitives)\n"); | |
| } | |
| static inline void mi_lock_release(mi_lock_t* lock) { | |
| mi_atomic_store_release(&lock->mutex, (uintptr_t)0); | |
| } | |
| static inline void mi_lock_init(mi_lock_t* lock) { | |
| mi_lock_release(lock); | |
| } | |
| static inline void mi_lock_done(mi_lock_t* lock) { | |
| (void)(lock); | |
| } | |
| typedef struct mi_atomic_once_s { | |
| _Atomic(uintptr_t) tid; | |
| mi_lock_t lock; | |
| } mi_atomic_once_t; | |
| // Returns `true` only on the first invocation, signifying we can execute an action once. | |
| // If it returns `true`, the caller should call `_mi_atomic_once_release` after performing the action. | |
| // Other threads (than the initial thread that entered) will block until `_mi_atomic_once_release` has been called. | |
| bool _mi_atomic_once_enter(mi_atomic_once_t* once); // defined in `libc.c` | |
| void _mi_atomic_once_release(mi_atomic_once_t* once); // defined in `libc.c` | |
Xet Storage Details
- Size:
- 21.8 kB
- Xet hash:
- ed8a441110aad50b531dfa78479cb019170ee9041c653d5367b199c643c25435
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.