Hi, Ben.
So, what about this patch? It's still not in mail-list, I guess,
and still applicable.
Best regards, Ilya Maximets.
On 15.07.2016 15:44, Ilya Maximets wrote:
> 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 <[email protected]>
> ---
> Version 4:
> * Added detailed describtion of rwlock states and possible
> operations in each state.
>
> Version 3:
> * First version of this patch.
>
> lib/fat-rwlock.c | 56
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
> lib/fat-rwlock.h | 33 +++++++++++++++++++++++++++++++++
> 2 files changed, 87 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..15514fc 100644
> --- a/lib/fat-rwlock.h
> +++ b/lib/fat-rwlock.h
> @@ -46,4 +46,37 @@ 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.
> + *
> + * Detailed description:
> + *
> -----------------------------------------------------------------------------
> + * STATE | POSSIBLE OPERATION | RESULTED STATE
> + *
> -----------------------------------------------------------------------------
> + * unlocked fat_rwlock_rdlock read-locked with depth =
> 1
> + * fat_rwlock_wrlock write-locked with depth
> = 1
> + *
> + * read-locked with depth = 1 fat_rwlock_rdlock read-locked with depth =
> 2
> + * fat_rwlock_unlock unlocked
> + * fat_rwlock_upgrade write-locked with depth
> = 1
> + *
> + * read-locked with depth = N fat_rwlock_rdlock read-locked with depth =
> N+1
> + * fat_rwlock_unlock read-locked with depth =
> N-1
> + * fat_rwlock_upgrade write-locked with depth
> = N
> + *
> + * write-locked with depth = 1 fat_rwlock_unlock unlocked
> + * fat_rwlock_downgrade read-locked with depth =
> 1
> + *
> + * write-locked with depth = N fat_rwlock_unlock read-locked with depth =
> N-1
> + * fat_rwlock_downgrade read-locked with depth =
> N
> + *
> -----------------------------------------------------------------------------
> + *
> + * 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 */
>
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev