From: Grigory Vasilyev h0tc0d3@gmail.com
--- include/Makefile.in | 1 + include/wine/mutex.h | 155 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 include/wine/mutex.h
diff --git a/include/Makefile.in b/include/Makefile.in index bbf28cfc87e..84742dded5c 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -920,6 +920,7 @@ SOURCES = \ wine/mmsystem16.h \ wine/mscvpdb.h \ wine/mssign.h \ + wine/mutex.h \ wine/nsi.h \ wine/orpc.idl \ wine/plugplay.idl \ diff --git a/include/wine/mutex.h b/include/wine/mutex.h new file mode 100644 index 00000000000..9998a43e492 --- /dev/null +++ b/include/wine/mutex.h @@ -0,0 +1,155 @@ +#ifndef __WINE_WINE_MUTEX_H +#define __WINE_WINE_MUTEX_H + +#if defined(WINE_USE_ATOMIC_LOCKS) && defined(__STDC_VERSION__) \ + && __STDC_VERSION__ >= 201112L +#ifndef __STDC_NO_ATOMICS__ + +#include <errno.h> +#include <stdint.h> +#include <unistd.h> +#include <stdatomic.h> +#include <sys/types.h> +#include <sys/syscall.h> +#include <linux/futex.h> + +#define __wine_mutex_unlikely(cond) __builtin_expect((cond), 0) +#define __wine_mutex_likely(cond) __builtin_expect((cond), 1) + +typedef _Atomic uint32_t WINE_MUTEX_TYPE; // futex only can use 32-bit int + +#if __STDC_VERSION__ >= 201710L +#define WINE_MUTEX_INIT 0 +#else +#define WINE_MUTEX_INIT ATOMIC_VAR_INIT(0) +#endif + +#define WINE_MUTEX_UNLOCKED 0 // Mutex Unlocked +#define WINE_MUTEX_LOCKED_NOWAIT 1 // Mutex Locked and Not Have Waiters +#define WINE_MUTEX_LOCKED_WAIT 2 // Mutex Locked and Have Waiters + +static inline int wine_mutex_lock(WINE_MUTEX_TYPE *mutex) +{ + uint32_t expected = WINE_MUTEX_UNLOCKED; + + if (!atomic_compare_exchange_strong(mutex, &expected, + WINE_MUTEX_LOCKED_NOWAIT)) { + if (expected == WINE_MUTEX_LOCKED_NOWAIT) { + expected = atomic_exchange(mutex, WINE_MUTEX_LOCKED_WAIT); + } + while (expected != WINE_MUTEX_UNLOCKED) { + syscall(SYS_futex, mutex, FUTEX_WAIT, WINE_MUTEX_LOCKED_WAIT, NULL, + NULL, 0); + expected = atomic_exchange(mutex, WINE_MUTEX_LOCKED_WAIT); + } + } + + return 0; +} + +static inline int wine_mutex_unlock(WINE_MUTEX_TYPE *mutex) +{ + if (atomic_load(mutex) + && atomic_fetch_sub(mutex, 1) == WINE_MUTEX_LOCKED_WAIT) { + atomic_store(mutex, WINE_MUTEX_UNLOCKED); + syscall(SYS_futex, mutex, FUTEX_WAKE, 1, NULL, NULL, 0); + } + + return 0; +} + +#define WINE_MUTEX_LOCK(__MUTEX__) wine_mutex_lock(__MUTEX__) +#define WINE_MUTEX_UNLOCK(__MUTEX__) wine_mutex_unlock(__MUTEX__) +#define WINE_MUTEX_DESTROY(__MUTEX__) wine_mutex_unlock(__MUTEX__) + +typedef struct +{ + _Atomic uint32_t lock; + _Atomic pthread_t owner; + _Atomic size_t count; +} WINE_MUTEX_RECURSIVE_TYPE __attribute__((aligned(16))); + +static inline int wine_mutex_recursive_init(WINE_MUTEX_RECURSIVE_TYPE *mutex) +{ + atomic_store(&mutex->lock, 0); + atomic_store(&mutex->owner, 0); + atomic_store(&mutex->count, 0); + + return 0; +} + +static inline int wine_mutex_recursive_lock(WINE_MUTEX_RECURSIVE_TYPE *mutex) +{ + uint32_t expected = WINE_MUTEX_LOCKED_NOWAIT; + pthread_t tid = pthread_self(); + + if (atomic_load(&mutex->owner) == tid) { + // Check counter for overflow + if (__wine_mutex_unlikely(atomic_load(&mutex->count) + 1 == 0)) + return EAGAIN; + atomic_fetch_add(&mutex->count, 1); + return 0; + } + + while (expected != WINE_MUTEX_UNLOCKED) { + syscall(SYS_futex, &mutex->lock, FUTEX_WAIT, WINE_MUTEX_LOCKED_NOWAIT, + NULL, NULL, 0); + expected = atomic_exchange(&mutex->lock, WINE_MUTEX_LOCKED_NOWAIT); + } + + atomic_store(&mutex->count, 1); + atomic_store(&mutex->owner, tid); + + return 0; +} + +static inline int wine_mutex_recursive_unlock(WINE_MUTEX_RECURSIVE_TYPE *mutex) +{ + pthread_t tid = pthread_self(); + + if (atomic_load(&mutex->owner) != tid) + return EPERM; + + if (atomic_fetch_sub(&mutex->count, 1) <= 1) { + atomic_store(&mutex->owner, 0); + atomic_store(&mutex->lock, WINE_MUTEX_UNLOCKED); + syscall(SYS_futex, &mutex->lock, FUTEX_WAKE, 1, NULL, NULL, 0); + } + + return 0; +} + +#define WINE_MUTEX_RECURSIVE_INIT(__MUTEX__) \ + wine_mutex_recursive_init(__MUTEX__) +#define WINE_MUTEX_RECURSIVE_LOCK(__MUTEX__) \ + wine_mutex_recursive_lock(__MUTEX__) +#define WINE_MUTEX_RECURSIVE_UNLOCK(__MUTEX__) \ + wine_mutex_recursive_unlock(__MUTEX__) +#define WINE_MUTEX_RECURSIVE_DESTROY(__MUTEX__) \ + wine_mutex_recursive_init(__MUTEX__) + +#else +#error C11 Atomic operations not supported. Compiler defined __STDC_NO_ATOMICS__. +#endif +#else +#error C11 Atomic operations not supported. C version is lower than C11 or WINE_USE_ATOMIC_LOCKS not defined. +#define WINE_MUTEX_TYPE pthread_mutex_t +#define WINE_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER +#define WINE_MUTEX_LOCK(__MUTEX__) pthread_mutex_lock(__MUTEX__) +#define WINE_MUTEX_UNLOCK(__MUTEX__) pthread_mutex_unlock(__MUTEX__) +#define WINE_MUTEX_DESTROY(__MUTEX__) pthread_mutex_destroy(__MUTEX__) +#define WINE_MUTEX_RECURSIVE_TYPE pthread_mutex_t +#define WINE_MUTEX_RECURSIVE_INIT(__MUTEX__) \ + do { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init(&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init(__MUTEX__, &attr); \ + pthread_mutexattr_destroy(&attr); \ + } while (0) +#define WINE_MUTEX_RECURSIVE_LOCK(__MUTEX__) pthread_mutex_lock(__MUTEX__) +#define WINE_MUTEX_RECURSIVE_UNLOCK(__MUTEX__) pthread_mutex_unlock(__MUTEX__) +#define WINE_MUTEX_RECURSIVE_DESTROY(__MUTEX__) pthread_mutex_destroy(__MUTEX__) +#endif + +#endif /* __WINE_WINE_MUTEX_H */