From: Olivier Houchard Date: Wed, 26 Mar 2025 12:11:30 +0000 (+0000) Subject: MINOR: threads: Add HA_RWLOCK_TRYRDTOWR() X-Git-Tag: v3.2-dev9~26 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ba521a1d883b4f996e2bda3b773521be8a5d8c90;p=thirdparty%2Fhaproxy.git MINOR: threads: Add HA_RWLOCK_TRYRDTOWR() Add HA_RWLOCK_TRYRDTOWR(), that tries to upgrade a lock from reader to writer, and fails if any seeker or writer already holds it. --- diff --git a/include/haproxy/thread.h b/include/haproxy/thread.h index d99146766..c71cd25ab 100644 --- a/include/haproxy/thread.h +++ b/include/haproxy/thread.h @@ -87,6 +87,7 @@ enum { tgid = 1 }; #define HA_RWLOCK_SKUNLOCK(lbl,l) do { /* do nothing */ } while(0) #define HA_RWLOCK_TRYSKLOCK(lbl,l) ({ 0; }) #define HA_RWLOCK_TRYRDTOSK(lbl,l) ({ 0; }) +#define HA_RWLOCK_TRYRDTOWR(lbl,l) ({ 0; }) #define ha_sigmask(how, set, oldset) sigprocmask(how, set, oldset) @@ -327,6 +328,7 @@ static inline unsigned long thread_isolated() #define HA_RWLOCK_SKUNLOCK(lbl,l) pl_drop_s(l) /* S --> N */ #define HA_RWLOCK_TRYSKLOCK(lbl,l) (!pl_try_s(l)) /* N -?> S */ #define HA_RWLOCK_TRYRDTOSK(lbl,l) (!pl_try_rtos(l)) /* R -?> S */ +#define HA_RWLOCK_TRYRDTOWR(lbl, l) (!pl_try_rtow(l)) /* R -?> W */ #else /* !defined(DEBUG_THREAD) && !defined(DEBUG_FULL) */ @@ -356,6 +358,7 @@ static inline unsigned long thread_isolated() #define __RWLOCK_SKUNLOCK(l) pl_drop_s(l) /* S --> N */ #define __RWLOCK_TRYSKLOCK(l) (!pl_try_s(l)) /* N -?> S */ #define __RWLOCK_TRYRDTOSK(l) (!pl_try_rtos(l)) /* R -?> S */ +#define __RWLOCK_TRYRDTOWR(l) (!pl_try_rtow(l)) /* R -?> W */ #define HA_SPIN_INIT(l) __spin_init(l) #define HA_SPIN_DESTROY(l) __spin_destroy(l) @@ -381,6 +384,7 @@ static inline unsigned long thread_isolated() #define HA_RWLOCK_SKUNLOCK(lbl,l) __ha_rwlock_skunlock(lbl, l, __func__, __FILE__, __LINE__) #define HA_RWLOCK_TRYSKLOCK(lbl,l) __ha_rwlock_trysklock(lbl, l, __func__, __FILE__, __LINE__) #define HA_RWLOCK_TRYRDTOSK(lbl,l) __ha_rwlock_tryrdtosk(lbl, l, __func__, __FILE__, __LINE__) +#define HA_RWLOCK_TRYRDTOWR(lbl,l) __ha_rwlock_tryrdtowr(lbl, l, __func__, __FILE__, __LINE__) /* Following functions are used to collect some stats about locks. We wrap * pthread functions to known how much time we wait in a lock. */ @@ -413,6 +417,8 @@ int __ha_rwlock_trysklock(enum lock_label lbl, struct ha_rwlock *l, const char *func, const char *file, int line); int __ha_rwlock_tryrdtosk(enum lock_label lbl, struct ha_rwlock *l, const char *func, const char *file, int line); +int __ha_rwlock_tryrdtowr(enum lock_label lbl, struct ha_rwlock *l, + const char *func, const char *file, int line); void __spin_init(struct ha_spinlock *l); void __spin_destroy(struct ha_spinlock *l); void __spin_lock(enum lock_label lbl, struct ha_spinlock *l, diff --git a/src/thread.c b/src/thread.c index 3ca0b5a4e..4b3e72b20 100644 --- a/src/thread.c +++ b/src/thread.c @@ -998,6 +998,46 @@ int __ha_rwlock_tryrdtosk(enum lock_label lbl, struct ha_rwlock *l, return r; } +int __ha_rwlock_tryrdtowr(enum lock_label lbl, struct ha_rwlock *l, + const char *func, const char *file, int line) +{ + ulong tbit = (ti && ti->ltid_bit) ? ti->ltid_bit : 1; + struct ha_rwlock_state *st = &l->info.st[tgid-1]; + uint64_t start_time; + uint bucket; + int r; + + if ((st->cur_writer | st->cur_seeker) & tbit) + abort(); + + if (!(st->cur_readers & tbit)) + abort(); + + HA_ATOMIC_OR(&st->wait_writers, tbit); + + start_time = -now_mono_time(); + r = __RWLOCK_TRYRDTOWR(&l->lock); + start_time += now_mono_time(); + + if (likely(!r)) { + /* got the lock ! */ + HA_ATOMIC_ADD(&lock_stats_sk[lbl].nsec_wait, start_time); + + start_time &= 0x3fffffff; // keep values below 1 billion only + bucket = flsnz((uint32_t)start_time ? (uint32_t)start_time : 1) - 1; + HA_ATOMIC_INC(&lock_stats_sk[lbl].buckets[bucket]); + HA_ATOMIC_OR(&st->cur_writer, tbit); + HA_ATOMIC_AND(&st->cur_readers, ~tbit); + l->info.last_location.function = func; + l->info.last_location.file = file; + l->info.last_location.line = line; + } + + HA_ATOMIC_AND(&st->wait_writers, ~tbit); + return r; +} + + void __spin_init(struct ha_spinlock *l) { memset(l, 0, sizeof(struct ha_spinlock));