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.
This commit is contained in:
Olivier Houchard 2025-03-26 12:11:30 +00:00 committed by Olivier Houchard
parent 2a9436f96b
commit ba521a1d88
2 changed files with 46 additions and 0 deletions

View file

@ -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,

View file

@ -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));