]> 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>
Wed, 19 Dec 2018 13:34:10 +0000 (14:34 +0100)
lib/isc/include/isc/rwlock.h
lib/isc/rwlock.c

index 50d06d72d007c782b1b479d37ba64470a9131aff..e94daa996fa1775a80ad85fcf528630a85b63ace 100644 (file)
@@ -36,6 +36,7 @@ typedef enum {
 
 struct isc_rwlock {
        pthread_rwlock_t        rwlock;
+       atomic_bool             downgrade;
 };
 
 #else /* HAVE_PTHREAD_RWLOCK_RDLOCK */
@@ -44,7 +45,7 @@ struct isc_rwlock {
        /* Unlocked. */
        unsigned int            magic;
        isc_mutex_t             lock;
-       int32_t         spins;
+       int32_t                 spins;
 
        /*
         * When some atomic instructions with hardware assistance are
index 79c344853568c8cd8ec48f1482a5f1999f2aa523..c788b1b8c80f16dfd1ab588a070af746121ae0d6 100644 (file)
@@ -38,15 +38,31 @@ 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(&rwl->downgrade)) {
+                               isc_rwlock_unlock(rwl, type);
+                               while (atomic_load(&rwl->downgrade));
+                               continue;
+                       }
+                       break;
+               }
+               break;
+       default:
+               INSIST(0);
+               ISC_UNREACHABLE();
        }
        return (ISC_R_SUCCESS);
 }
@@ -60,6 +76,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 +108,10 @@ isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
 
 void
 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
+       atomic_store(&rwl->downgrade, true);
        isc_rwlock_unlock(rwl, isc_rwlocktype_write);
        isc_rwlock_lock(rwl, isc_rwlocktype_read);
+       atomic_store(&rwl->downgrade, false);
 }
 
 void