]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Implement isc_rwlock_downgrade using pthreads and single atomic_bool
authorOndřej Surý <ondrej@sury.org>
Thu, 22 Nov 2018 10:42:12 +0000 (11:42 +0100)
committerOndřej Surý <ondrej@sury.org>
Thu, 30 May 2019 14:10:16 +0000 (16:10 +0200)
lib/isc/include/isc/rwlock.h
lib/isc/rwlock.c

index 50d06d72d007c782b1b479d37ba64470a9131aff..5551c1b6239d2196dd1bd8bbbb1e16c13609e499 100644 (file)
@@ -36,6 +36,7 @@ typedef enum {
 
 struct isc_rwlock {
        pthread_rwlock_t        rwlock;
+       atomic_bool             downgrade;
 };
 
 #else /* HAVE_PTHREAD_RWLOCK_RDLOCK */
index 2619619d1b4c1502fe06e4c52a4ca85b92e85762..7d5e14ea4eb18587be37e6d1ffa6a08b275c1de4 100644 (file)
@@ -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