diff --git a/Makefile b/Makefile index eba16e77e7..05c79d02bf 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ # USE_OT : enable the OpenTracing filter # USE_MEMORY_PROFILING : enable the memory profiler. Linux-glibc only. # USE_LIBATOMIC : force to link with/without libatomic. Automatic. +# USE_PTHREAD_EMULATION: replace pthread's rwlocks with ours # # Options can be forced by specifying "USE_xxx=1" or can be disabled by using # "USE_xxx=" (empty string). The list of enabled and disabled options for a @@ -337,7 +338,7 @@ LDFLAGS = $(ARCH_FLAGS) -g # the reported build options. use_opts = USE_EPOLL USE_KQUEUE USE_NETFILTER \ USE_PCRE USE_PCRE_JIT USE_PCRE2 USE_PCRE2_JIT USE_POLL \ - USE_THREAD USE_BACKTRACE \ + USE_THREAD USE_PTHREAD_EMULATION USE_BACKTRACE \ USE_STATIC_PCRE USE_STATIC_PCRE2 USE_TPROXY USE_LINUX_TPROXY \ USE_LINUX_SPLICE USE_LIBCRYPT USE_CRYPT_H USE_ENGINE \ USE_GETADDRINFO USE_OPENSSL USE_LUA USE_ACCEPT4 \ diff --git a/src/thread.c b/src/thread.c index cc983a2612..c8462291c8 100644 --- a/src/thread.c +++ b/src/thread.c @@ -968,6 +968,75 @@ void __spin_unlock(enum lock_label lbl, struct ha_spinlock *l, #endif // defined(DEBUG_THREAD) || defined(DEBUG_FULL) + +#if defined(USE_PTHREAD_EMULATION) + +/* pthread rwlock emulation using plocks (to avoid expensive futexes). + * these are a direct mapping on Progressive Locks, with the exception that + * since there's a common unlock operation in pthreads, we need to know if + * we need to unlock for reads or writes, so we set the topmost bit to 1 when + * a write lock is acquired to indicate that a write unlock needs to be + * performed. It's not a problem since this bit will never be used given that + * haproxy won't support as many threads as the plocks. + * + * The storage is the pthread_rwlock_t cast as an ulong + */ + +int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr) +{ + ulong *lock = (ulong *)rwlock; + + *lock = 0; + return 0; +} + +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) +{ + ulong *lock = (ulong *)rwlock; + + *lock = 0; + return 0; +} + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ + pl_lorw_rdlock((unsigned long *)rwlock); + return 0; +} + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) +{ + return !!pl_cmpxchg((unsigned long *)rwlock, 0, PLOCK_LORW_SHR_BASE); +} + +int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abstime) +{ + return pthread_rwlock_tryrdlock(rwlock); +} + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) +{ + pl_lorw_wrlock((unsigned long *)rwlock); + return 0; +} + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) +{ + return !!pl_cmpxchg((unsigned long *)rwlock, 0, PLOCK_LORW_EXC_BASE); +} + +int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abstime) +{ + return pthread_rwlock_trywrlock(rwlock); +} + +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +{ + pl_lorw_unlock((unsigned long *)rwlock); + return 0; +} +#endif // defined(USE_PTHREAD_EMULATION) + /* Depending on the platform and how libpthread was built, pthread_exit() may * involve some code in libgcc_s that would be loaded on exit for the first * time, causing aborts if the process is chrooted. It's harmless bit very