New functions 'fat_rwlock_{up,down}grade()' introduced to allow upgrading read-lock to write-lock and downgrading it back.
Signed-off-by: Ilya Maximets <i.maxim...@samsung.com> --- lib/fat-rwlock.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/fat-rwlock.h | 11 +++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/lib/fat-rwlock.c b/lib/fat-rwlock.c index 2f42b05..86a4693 100644 --- a/lib/fat-rwlock.c +++ b/lib/fat-rwlock.c @@ -53,10 +53,13 @@ struct fat_rwlock_slot { * * - UINT_MAX: This thread has the write-lock on 'rwlock' and holds * 'mutex' (plus the 'mutex' of all of 'rwlock''s other slots). + * 'upgrade_depth' means the depth of read-lock on which it was + * upgraded to write-lock. * * Accessed only by the slot's own thread, so no synchronization is * needed. */ unsigned int depth; + unsigned int upgrade_depth; }; static void @@ -127,6 +130,7 @@ fat_rwlock_get_slot__(struct fat_rwlock *rwlock) slot->rwlock = rwlock; ovs_mutex_init(&slot->mutex); slot->depth = 0; + slot->upgrade_depth = 0; ovs_mutex_lock(&rwlock->mutex); ovs_list_push_back(&rwlock->threads, &slot->list_node); @@ -236,6 +240,7 @@ fat_rwlock_wrlock(const struct fat_rwlock *rwlock_) ovs_assert(!this->depth); this->depth = UINT_MAX; + this->upgrade_depth = 1; ovs_mutex_lock(&rwlock->mutex); LIST_FOR_EACH (slot, list_node, &rwlock->threads) { @@ -257,11 +262,13 @@ fat_rwlock_unlock(const struct fat_rwlock *rwlock_) switch (this->depth) { case UINT_MAX: + this->depth = this->upgrade_depth - 1; LIST_FOR_EACH (slot, list_node, &rwlock->threads) { - ovs_mutex_unlock(&slot->mutex); + if (slot != this || this->depth == 0) { + ovs_mutex_unlock(&slot->mutex); + } } ovs_mutex_unlock(&rwlock->mutex); - this->depth = 0; break; case 0: @@ -275,3 +282,48 @@ fat_rwlock_unlock(const struct fat_rwlock *rwlock_) break; } } + +/* Upgrades last taken read-lock to write-lock. + * Not thread-safe with 'fat_rwlock_wrlock' and concurrent upgrades. */ +void +fat_rwlock_upgrade(const struct fat_rwlock *rwlock_) + OVS_NO_THREAD_SAFETY_ANALYSIS +{ + struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_); + struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock); + struct fat_rwlock_slot *slot; + + ovs_assert(this->depth && this->depth != UINT_MAX); + + this->upgrade_depth = this->depth; + this->depth = UINT_MAX; + + ovs_mutex_lock(&rwlock->mutex); + LIST_FOR_EACH (slot, list_node, &rwlock->threads) { + if (slot != this) { + ovs_mutex_lock(&slot->mutex); + } + } +} + +/* Downgrades write-lock to read-lock. */ +void +fat_rwlock_downgrade(const struct fat_rwlock *rwlock_) + OVS_NO_THREAD_SAFETY_ANALYSIS +{ + struct fat_rwlock *rwlock = CONST_CAST(struct fat_rwlock *, rwlock_); + struct fat_rwlock_slot *this = fat_rwlock_get_slot__(rwlock); + struct fat_rwlock_slot *slot; + + ovs_assert(this->depth == UINT_MAX); + + this->depth = this->upgrade_depth; + this->upgrade_depth = 0; + + LIST_FOR_EACH (slot, list_node, &rwlock->threads) { + if (slot != this) { + ovs_mutex_unlock(&slot->mutex); + } + } + ovs_mutex_unlock(&rwlock->mutex); +} diff --git a/lib/fat-rwlock.h b/lib/fat-rwlock.h index 181fa92..70d5e95 100644 --- a/lib/fat-rwlock.h +++ b/lib/fat-rwlock.h @@ -46,4 +46,15 @@ int fat_rwlock_tryrdlock(const struct fat_rwlock *rwlock) void fat_rwlock_wrlock(const struct fat_rwlock *rwlock) OVS_ACQ_WRLOCK(rwlock); void fat_rwlock_unlock(const struct fat_rwlock *rwlock) OVS_RELEASES(rwlock); +/* + * Following functions used to upgrade last taken read-lock to write-lock and + * downgrade it back to read-lock. Upgrading/downgrading doesn't change depth + * of recursive locking. + * + * Upgrading is NOT thread-safe operation, so, the caller must be sure that + * it is the only thread that wants to acquire write-lock. + */ +void fat_rwlock_upgrade(const struct fat_rwlock *rwlock); +void fat_rwlock_downgrade(const struct fat_rwlock *rwlock); + #endif /* fat-rwlock.h */ -- 2.7.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev