add initial primitive api for locks

This commit is contained in:
Daan Leijen 2024-06-01 16:45:20 -07:00
parent d9aa19a763
commit 0b3cd51249
11 changed files with 208 additions and 48 deletions

View file

@ -8,6 +8,17 @@ terms of the MIT license. A copy of the license can be found in the file
#ifndef MIMALLOC_ATOMIC_H #ifndef MIMALLOC_ATOMIC_H
#define MIMALLOC_ATOMIC_H #define MIMALLOC_ATOMIC_H
// include windows.h or pthreads.h
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#elif !defined(_WIN32) && (defined(__EMSCRIPTEN_SHARED_MEMORY__) || !defined(__wasi__))
#define MI_USE_PTHREADS
#include <pthread.h>
#endif
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// Atomics // Atomics
// We need to be portable between C, C++, and MSVC. // We need to be portable between C, C++, and MSVC.
@ -133,10 +144,6 @@ static inline void mi_atomic_maxi64_relaxed(volatile int64_t* p, int64_t x) {
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
// Legacy MSVC plain C compilation wrapper that uses Interlocked operations to model C11 atomics. // Legacy MSVC plain C compilation wrapper that uses Interlocked operations to model C11 atomics.
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <intrin.h> #include <intrin.h>
#ifdef _WIN64 #ifdef _WIN64
typedef LONG64 msc_intptr_t; typedef LONG64 msc_intptr_t;
@ -329,10 +336,6 @@ static inline void mi_atomic_yield(void) {
std::this_thread::yield(); std::this_thread::yield();
} }
#elif defined(_WIN32) #elif defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
static inline void mi_atomic_yield(void) { static inline void mi_atomic_yield(void) {
YieldProcessor(); YieldProcessor();
} }

View file

@ -53,11 +53,6 @@ terms of the MIT license. A copy of the license can be found in the file
#define mi_decl_externc #define mi_decl_externc
#endif #endif
// pthreads
#if !defined(_WIN32) && !defined(__wasi__)
#define MI_USE_PTHREADS
#include <pthread.h>
#endif
// "options.c" // "options.c"
void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message); void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message);

View file

@ -114,6 +114,24 @@ void _mi_prim_thread_done_auto_done(void);
// Called when the default heap for a thread changes // Called when the default heap for a thread changes
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap); void _mi_prim_thread_associate_default_heap(mi_heap_t* heap);
// Locks are only used if abandoned segment visiting is permitted
#if defined(_WIN32)
#define mi_lock_t CRITICAL_SECTION
#elif defined(MI_USE_PTHREADS)
#define mi_lock_t pthread_mutex_t
#else
#define mi_lock_t _Atomic(uintptr_t)
#endif
// Take a lock (blocking). Return `true` on success.
bool _mi_prim_lock(mi_lock_t* lock);
// Try to take lock and return `true` if successful.
bool _mi_prim_try_lock(mi_lock_t* lock);
// Release a lock.
void _mi_prim_unlock(mi_lock_t* lock);
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Thread id: `_mi_prim_thread_id()` // Thread id: `_mi_prim_thread_id()`
@ -235,10 +253,6 @@ static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept {
#elif defined(_WIN32) #elif defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept { static inline mi_threadid_t _mi_prim_thread_id(void) mi_attr_noexcept {
// Windows: works on Intel and ARM in both 32- and 64-bit // Windows: works on Intel and ARM in both 32- and 64-bit
return (uintptr_t)NtCurrentTeb(); return (uintptr_t)NtCurrentTeb();
@ -370,4 +384,6 @@ static inline mi_heap_t* mi_prim_get_default_heap(void) {
#endif // MIMALLOC_PRIM_H #endif // MIMALLOC_PRIM_H

View file

@ -82,10 +82,6 @@ defined, undefined, or not accessible at all:
#define MI_TRACK_HEAP_DESTROY 1 #define MI_TRACK_HEAP_DESTROY 1
#define MI_TRACK_TOOL "ETW" #define MI_TRACK_TOOL "ETW"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include "../src/prim/windows/etw.h" #include "../src/prim/windows/etw.h"
#define mi_track_init() EventRegistermicrosoft_windows_mimalloc(); #define mi_track_init() EventRegistermicrosoft_windows_mimalloc();

View file

@ -362,7 +362,7 @@ mi_decl_nodiscard mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_
#ifndef PATH_MAX #ifndef PATH_MAX
#define PATH_MAX MAX_PATH #define PATH_MAX MAX_PATH
#endif #endif
#include <windows.h>
mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept { mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept {
// todo: use GetFullPathNameW to allow longer file names // todo: use GetFullPathNameW to allow longer file names
char buf[PATH_MAX]; char buf[PATH_MAX];

View file

@ -200,7 +200,7 @@ bool _mi_prim_random_buf(void* buf, size_t buf_len) {
// Thread init/done // Thread init/done
//---------------------------------------------------------------- //----------------------------------------------------------------
#ifdef __EMSCRIPTEN_SHARED_MEMORY__ #if defined(MI_USE_PTHREADS)
// use pthread local storage keys to detect thread ending // use pthread local storage keys to detect thread ending
// (and used with MI_TLS_PTHREADS for the default heap) // (and used with MI_TLS_PTHREADS for the default heap)
@ -242,3 +242,50 @@ void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
} }
#endif #endif
//----------------------------------------------------------------
// Locks
//----------------------------------------------------------------
#if defined(MI_USE_PTHREADS)
bool _mi_prim_lock(mi_lock_t* lock) {
return (pthread_mutex_lock(lock) == 0);
}
bool _mi_prim_try_lock(mi_lock_t* lock) {
return (pthread_mutex_trylock(lock) == 0);
}
void _mi_prim_unlock(mi_lock_t* lock) {
pthread_mutex_unlock(lock);
}
#else
#include <emscripten.h>
// fall back to poor man's locks.
bool _mi_prim_lock(mi_lock_t* lock) {
for(int i = 0; i < 1000; i++) { // for at most 1 second?
if (_mi_prim_try_lock(lock)) return true;
if (i < 25) {
mi_atomic_yield(); // first yield a bit
}
else {
emscripten_sleep(1); // then sleep for 1ms intervals
}
}
return true;
}
bool _mi_prim_try_lock(mi_lock_t* lock) {
uintptr_t expected = 0;
return mi_atomic_cas_strong_acq_rel(lock,&expected,(uintptr_t)1);
}
void _mi_prim_unlock(mi_lock_t* lock) {
mi_atomic_store_release(lock,(uintptr_t)0);
}
#endif

View file

@ -880,3 +880,49 @@ void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
} }
#endif #endif
//----------------------------------------------------------------
// Locks
//----------------------------------------------------------------
#if defined(MI_USE_PTHREADS)
bool _mi_prim_lock(mi_lock_t* lock) {
return (pthread_mutex_lock(lock) == 0);
}
bool _mi_prim_try_lock(mi_lock_t* lock) {
return (pthread_mutex_trylock(lock) == 0);
}
void _mi_prim_unlock(mi_lock_t* lock) {
pthread_mutex_unlock(lock);
}
#else
// fall back to poor man's locks.
bool _mi_prim_lock(mi_lock_t* lock) {
for(int i = 0; i < 1000; i++) { // for at most 1 second?
if (_mi_prim_try_lock(lock)) return true;
if (i < 25) {
mi_atomic_yield(); // first yield a bit
}
else {
usleep(1000); // then sleep for 1ms intervals
}
}
return true;
}
bool _mi_prim_try_lock(mi_lock_t* lock) {
uintptr_t expected = 0;
return mi_atomic_cas_strong_acq_rel(lock,&expected,(uintptr_t)1);
}
void _mi_prim_unlock(mi_lock_t* lock) {
mi_atomic_store_release(lock,(uintptr_t)0);
}
#endif

View file

@ -278,3 +278,43 @@ void _mi_prim_thread_done_auto_done(void) {
void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) { void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
MI_UNUSED(heap); MI_UNUSED(heap);
} }
//----------------------------------------------------------------
// Locks
//----------------------------------------------------------------
#if defined(MI_USE_PTHREADS)
bool _mi_prim_lock(mi_lock_t* lock) {
return (pthread_mutex_lock(lock) == 0);
}
bool _mi_prim_try_lock(mi_lock_t* lock) {
return (pthread_mutex_trylock(lock) == 0);
}
void _mi_prim_unlock(mi_lock_t* lock) {
pthread_mutex_unlock(lock);
}
#else
// fall back to poor man's locks.
bool _mi_prim_lock(mi_lock_t* lock) {
for(int i = 0; i < 1000; i++) { // for at most 1 second?
if (_mi_prim_try_lock(lock)) return true;
mi_atomic_yield(); // this should never happen as wasi is single threaded?
}
return true;
}
bool _mi_prim_try_lock(mi_lock_t* lock) {
uintptr_t expected = 0;
return mi_atomic_cas_strong_acq_rel(lock,&expected,(uintptr_t)1);
}
void _mi_prim_unlock(mi_lock_t* lock) {
mi_atomic_store_release(lock,(uintptr_t)0);
}
#endif

View file

@ -468,7 +468,6 @@ mi_msecs_t _mi_prim_clock_now(void) {
// Process Info // Process Info
//---------------------------------------------------------------- //----------------------------------------------------------------
#include <windows.h>
#include <psapi.h> #include <psapi.h>
static mi_msecs_t filetime_msecs(const FILETIME* ftime) { static mi_msecs_t filetime_msecs(const FILETIME* ftime) {
@ -564,6 +563,23 @@ bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
} }
//----------------------------------------------------------------
// Locks
//----------------------------------------------------------------
bool _mi_prim_lock(mi_lock_t* lock) {
EnterCriticalSection(lock);
return true;
}
bool _mi_prim_try_lock(mi_lock_t* lock) {
return TryEnterCriticalSection(lock);
}
void _mi_prim_unlock(mi_lock_t* lock) {
LeaveCriticalSection(lock);
}
//---------------------------------------------------------------- //----------------------------------------------------------------
// Random // Random
@ -661,3 +677,4 @@ void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
} }
#endif #endif

View file

@ -19,7 +19,7 @@
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <windows.h>
static void msleep(unsigned long msecs) { Sleep(msecs); } static void msleep(unsigned long msecs) { Sleep(msecs); }
#else #else
#include <unistd.h> #include <unistd.h>

View file

@ -285,7 +285,7 @@ static void (*thread_entry_fun)(intptr_t) = &stress;
#ifdef _WIN32 #ifdef _WIN32
#include <Windows.h> #include <windows.h>
static DWORD WINAPI thread_entry(LPVOID param) { static DWORD WINAPI thread_entry(LPVOID param) {
thread_entry_fun((intptr_t)param); thread_entry_fun((intptr_t)param);