diff --git a/lib/isc/include/isc/rwlock.h b/lib/isc/include/isc/rwlock.h index 50d06d72d0..5551c1b623 100644 --- a/lib/isc/include/isc/rwlock.h +++ b/lib/isc/include/isc/rwlock.h @@ -36,6 +36,7 @@ typedef enum { struct isc_rwlock { pthread_rwlock_t rwlock; + atomic_bool downgrade; }; #else /* HAVE_PTHREAD_RWLOCK_RDLOCK */ diff --git a/lib/isc/rwlock.c b/lib/isc/rwlock.c index 2619619d1b..7d5e14ea4e 100644 --- a/lib/isc/rwlock.c +++ b/lib/isc/rwlock.c @@ -39,15 +39,32 @@ isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota, UNUSED(read_quota); UNUSED(write_quota); REQUIRE(pthread_rwlock_init(&rwl->rwlock, NULL) == 0); + atomic_init(&rwl->downgrade, false); return (ISC_R_SUCCESS); } isc_result_t isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { switch (type) { - case isc_rwlocktype_read: REQUIRE(pthread_rwlock_rdlock(&rwl->rwlock) == 0); break; - case isc_rwlocktype_write: REQUIRE(pthread_rwlock_wrlock(&rwl->rwlock) == 0); break; - default: INSIST(0); + case isc_rwlocktype_read: + REQUIRE(pthread_rwlock_rdlock(&rwl->rwlock) == 0); + break; + case isc_rwlocktype_write: + while (true) { + REQUIRE(pthread_rwlock_wrlock(&rwl->rwlock) == 0); + /* Unlock if in middle of downgrade operation */ + if (atomic_load_release(&rwl->downgrade)) { + REQUIRE(pthread_rwlock_unlock(&rwl->rwlock) + == 0); + while (atomic_load(&rwl->downgrade)); + continue; + } + break; + } + break; + default: + INSIST(0); + ISC_UNREACHABLE(); } return (ISC_R_SUCCESS); } @@ -61,6 +78,10 @@ isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { break; case isc_rwlocktype_write: ret = pthread_rwlock_trywrlock(&rwl->rwlock); + if ((ret == 0) && atomic_load(&rwl->downgrade)) { + isc_rwlock_unlock(rwl, type); + return (ISC_R_LOCKBUSY); + } break; default: INSIST(0); } @@ -88,8 +109,10 @@ isc_rwlock_tryupgrade(isc_rwlock_t *rwl) { void isc_rwlock_downgrade(isc_rwlock_t *rwl) { + atomic_store_acquire(&rwl->downgrade, true); isc_rwlock_unlock(rwl, isc_rwlocktype_write); isc_rwlock_lock(rwl, isc_rwlocktype_read); + atomic_store_acquire(&rwl->downgrade, false); } void