diff mbox

[1/4] fifolock: create rfifolock_is_locked helper

Message ID 1446044465-19312-2-git-send-email-den@openvz.org
State New
Headers show

Commit Message

Denis V. Lunev Oct. 28, 2015, 3:01 p.m. UTC
This helper is necessary to ensure locking constraints.

Signed-off-by: Denis V. Lunev <den@openvz.org>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Paolo Bonzini <pbonzini@redhat.com>
---
 include/qemu/rfifolock.h | 1 +
 util/rfifolock.c         | 9 +++++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

Comments

Stefan Hajnoczi Oct. 30, 2015, 3:41 p.m. UTC | #1
On Wed, Oct 28, 2015 at 06:01:02PM +0300, Denis V. Lunev wrote:
> +int  rfifolock_is_locked(RFifoLock *r);

Please use bool instead of int.

> diff --git a/util/rfifolock.c b/util/rfifolock.c
> index afbf748..8ac58cb 100644
> --- a/util/rfifolock.c
> +++ b/util/rfifolock.c
> @@ -48,7 +48,7 @@ void rfifolock_lock(RFifoLock *r)
>      /* Take a ticket */
>      unsigned int ticket = r->tail++;
>  
> -    if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
> +    if (r->nesting > 0 && rfifolock_is_locked(r)) {
>          r->tail--; /* put ticket back, we're nesting */
>      } else {
>          while (ticket != r->head) {
> @@ -69,10 +69,15 @@ void rfifolock_unlock(RFifoLock *r)
>  {
>      qemu_mutex_lock(&r->lock);
>      assert(r->nesting > 0);
> -    assert(qemu_thread_is_self(&r->owner_thread));
> +    assert(rfifolock_is_locked(r));
>      if (--r->nesting == 0) {
>          r->head++;
>          qemu_cond_broadcast(&r->cond);
>      }
>      qemu_mutex_unlock(&r->lock);
>  }
> +
> +int rfifolock_is_locked(RFifoLock *r)
> +{
> +    return qemu_thread_is_self(&r->owner_thread);
> +}

The function name confused me since "does the current thread hold the
lock?" != "does anyone currently hold the lock?".

I suggest:

bool rfifolock_held_by_current_thread(RFifoLock *r) {
    return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
}

Then the r->nesting > 0 testing can also be dropped by callers, which is
good since rfifolock_is_locked() does not return a meaningful result
when r->nesting == 0.
Denis V. Lunev Oct. 30, 2015, 8:30 p.m. UTC | #2
On 10/30/2015 06:41 PM, Stefan Hajnoczi wrote:
> On Wed, Oct 28, 2015 at 06:01:02PM +0300, Denis V. Lunev wrote:
>> +int  rfifolock_is_locked(RFifoLock *r);
> Please use bool instead of int.
>
>> diff --git a/util/rfifolock.c b/util/rfifolock.c
>> index afbf748..8ac58cb 100644
>> --- a/util/rfifolock.c
>> +++ b/util/rfifolock.c
>> @@ -48,7 +48,7 @@ void rfifolock_lock(RFifoLock *r)
>>       /* Take a ticket */
>>       unsigned int ticket = r->tail++;
>>   
>> -    if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
>> +    if (r->nesting > 0 && rfifolock_is_locked(r)) {
>>           r->tail--; /* put ticket back, we're nesting */
>>       } else {
>>           while (ticket != r->head) {
>> @@ -69,10 +69,15 @@ void rfifolock_unlock(RFifoLock *r)
>>   {
>>       qemu_mutex_lock(&r->lock);
>>       assert(r->nesting > 0);
>> -    assert(qemu_thread_is_self(&r->owner_thread));
>> +    assert(rfifolock_is_locked(r));
>>       if (--r->nesting == 0) {
>>           r->head++;
>>           qemu_cond_broadcast(&r->cond);
>>       }
>>       qemu_mutex_unlock(&r->lock);
>>   }
>> +
>> +int rfifolock_is_locked(RFifoLock *r)
>> +{
>> +    return qemu_thread_is_self(&r->owner_thread);
>> +}
> The function name confused me since "does the current thread hold the
> lock?" != "does anyone currently hold the lock?".
>
> I suggest:
>
> bool rfifolock_held_by_current_thread(RFifoLock *r) {
>      return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
> }
>
> Then the r->nesting > 0 testing can also be dropped by callers, which is
> good since rfifolock_is_locked() does not return a meaningful result
> when r->nesting == 0.
what about

rfifolock_is_owner() ?

Den
Denis V. Lunev Nov. 1, 2015, 1:55 p.m. UTC | #3
On 10/30/2015 06:41 PM, Stefan Hajnoczi wrote:
> On Wed, Oct 28, 2015 at 06:01:02PM +0300, Denis V. Lunev wrote:
>> +int  rfifolock_is_locked(RFifoLock *r);
> Please use bool instead of int.
>
>> diff --git a/util/rfifolock.c b/util/rfifolock.c
>> index afbf748..8ac58cb 100644
>> --- a/util/rfifolock.c
>> +++ b/util/rfifolock.c
>> @@ -48,7 +48,7 @@ void rfifolock_lock(RFifoLock *r)
>>       /* Take a ticket */
>>       unsigned int ticket = r->tail++;
>>   
>> -    if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
>> +    if (r->nesting > 0 && rfifolock_is_locked(r)) {
>>           r->tail--; /* put ticket back, we're nesting */
>>       } else {
>>           while (ticket != r->head) {
>> @@ -69,10 +69,15 @@ void rfifolock_unlock(RFifoLock *r)
>>   {
>>       qemu_mutex_lock(&r->lock);
>>       assert(r->nesting > 0);
>> -    assert(qemu_thread_is_self(&r->owner_thread));
>> +    assert(rfifolock_is_locked(r));
>>       if (--r->nesting == 0) {
>>           r->head++;
>>           qemu_cond_broadcast(&r->cond);
>>       }
>>       qemu_mutex_unlock(&r->lock);
>>   }
>> +
>> +int rfifolock_is_locked(RFifoLock *r)
>> +{
>> +    return qemu_thread_is_self(&r->owner_thread);
>> +}
> The function name confused me since "does the current thread hold the
> lock?" != "does anyone currently hold the lock?".
>
> I suggest:
>
> bool rfifolock_held_by_current_thread(RFifoLock *r) {
>      return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
> }
>
> Then the r->nesting > 0 testing can also be dropped by callers, which is
> good since rfifolock_is_locked() does not return a meaningful result
> when r->nesting == 0.
actually the function is broken in the current state:
     aio_context_acquire()
     aio_context_release()
     aio_context_is_locked() will return true.
the problem is that owner thread is not reset on lock release.

with your proposal the function become racy if called from outer
thread. I need to think a bit here.

May be we'll need 2 versions - locked and unlocked or
something other should be added. I am in progress of
invention :)

Den
Stefan Hajnoczi Nov. 2, 2015, 1:10 p.m. UTC | #4
On Fri, Oct 30, 2015 at 11:30:04PM +0300, Denis V. Lunev wrote:
> On 10/30/2015 06:41 PM, Stefan Hajnoczi wrote:
> >On Wed, Oct 28, 2015 at 06:01:02PM +0300, Denis V. Lunev wrote:
> >>+int  rfifolock_is_locked(RFifoLock *r);
> >Please use bool instead of int.
> >
> >>diff --git a/util/rfifolock.c b/util/rfifolock.c
> >>index afbf748..8ac58cb 100644
> >>--- a/util/rfifolock.c
> >>+++ b/util/rfifolock.c
> >>@@ -48,7 +48,7 @@ void rfifolock_lock(RFifoLock *r)
> >>      /* Take a ticket */
> >>      unsigned int ticket = r->tail++;
> >>-    if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
> >>+    if (r->nesting > 0 && rfifolock_is_locked(r)) {
> >>          r->tail--; /* put ticket back, we're nesting */
> >>      } else {
> >>          while (ticket != r->head) {
> >>@@ -69,10 +69,15 @@ void rfifolock_unlock(RFifoLock *r)
> >>  {
> >>      qemu_mutex_lock(&r->lock);
> >>      assert(r->nesting > 0);
> >>-    assert(qemu_thread_is_self(&r->owner_thread));
> >>+    assert(rfifolock_is_locked(r));
> >>      if (--r->nesting == 0) {
> >>          r->head++;
> >>          qemu_cond_broadcast(&r->cond);
> >>      }
> >>      qemu_mutex_unlock(&r->lock);
> >>  }
> >>+
> >>+int rfifolock_is_locked(RFifoLock *r)
> >>+{
> >>+    return qemu_thread_is_self(&r->owner_thread);
> >>+}
> >The function name confused me since "does the current thread hold the
> >lock?" != "does anyone currently hold the lock?".
> >
> >I suggest:
> >
> >bool rfifolock_held_by_current_thread(RFifoLock *r) {
> >     return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
> >}
> >
> >Then the r->nesting > 0 testing can also be dropped by callers, which is
> >good since rfifolock_is_locked() does not return a meaningful result
> >when r->nesting == 0.
> what about
> 
> rfifolock_is_owner() ?

Great!

Stefan
Stefan Hajnoczi Nov. 2, 2015, 1:12 p.m. UTC | #5
On Sun, Nov 01, 2015 at 04:55:51PM +0300, Denis V. Lunev wrote:
> On 10/30/2015 06:41 PM, Stefan Hajnoczi wrote:
> >On Wed, Oct 28, 2015 at 06:01:02PM +0300, Denis V. Lunev wrote:
> >>+int  rfifolock_is_locked(RFifoLock *r);
> >Please use bool instead of int.
> >
> >>diff --git a/util/rfifolock.c b/util/rfifolock.c
> >>index afbf748..8ac58cb 100644
> >>--- a/util/rfifolock.c
> >>+++ b/util/rfifolock.c
> >>@@ -48,7 +48,7 @@ void rfifolock_lock(RFifoLock *r)
> >>      /* Take a ticket */
> >>      unsigned int ticket = r->tail++;
> >>-    if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
> >>+    if (r->nesting > 0 && rfifolock_is_locked(r)) {
> >>          r->tail--; /* put ticket back, we're nesting */
> >>      } else {
> >>          while (ticket != r->head) {
> >>@@ -69,10 +69,15 @@ void rfifolock_unlock(RFifoLock *r)
> >>  {
> >>      qemu_mutex_lock(&r->lock);
> >>      assert(r->nesting > 0);
> >>-    assert(qemu_thread_is_self(&r->owner_thread));
> >>+    assert(rfifolock_is_locked(r));
> >>      if (--r->nesting == 0) {
> >>          r->head++;
> >>          qemu_cond_broadcast(&r->cond);
> >>      }
> >>      qemu_mutex_unlock(&r->lock);
> >>  }
> >>+
> >>+int rfifolock_is_locked(RFifoLock *r)
> >>+{
> >>+    return qemu_thread_is_self(&r->owner_thread);
> >>+}
> >The function name confused me since "does the current thread hold the
> >lock?" != "does anyone currently hold the lock?".
> >
> >I suggest:
> >
> >bool rfifolock_held_by_current_thread(RFifoLock *r) {
> >     return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
> >}
> >
> >Then the r->nesting > 0 testing can also be dropped by callers, which is
> >good since rfifolock_is_locked() does not return a meaningful result
> >when r->nesting == 0.
> actually the function is broken in the current state:
>     aio_context_acquire()
>     aio_context_release()
>     aio_context_is_locked() will return true.
> the problem is that owner thread is not reset on lock release.

The owner thread field is only valid when nesting > 0.

> with your proposal the function become racy if called from outer
> thread. I need to think a bit here.

This is thread-safe:

bool owner;

qemu_mutex_lock(&r->lock);
owner = r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
qemu_mutex_unlock(&r->lock);

return owner;
Denis V. Lunev Nov. 2, 2015, 1:39 p.m. UTC | #6
On 11/02/2015 04:12 PM, Stefan Hajnoczi wrote:
> On Sun, Nov 01, 2015 at 04:55:51PM +0300, Denis V. Lunev wrote:
>> On 10/30/2015 06:41 PM, Stefan Hajnoczi wrote:
>>> On Wed, Oct 28, 2015 at 06:01:02PM +0300, Denis V. Lunev wrote:
>>>> +int  rfifolock_is_locked(RFifoLock *r);
>>> Please use bool instead of int.
>>>
>>>> diff --git a/util/rfifolock.c b/util/rfifolock.c
>>>> index afbf748..8ac58cb 100644
>>>> --- a/util/rfifolock.c
>>>> +++ b/util/rfifolock.c
>>>> @@ -48,7 +48,7 @@ void rfifolock_lock(RFifoLock *r)
>>>>       /* Take a ticket */
>>>>       unsigned int ticket = r->tail++;
>>>> -    if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
>>>> +    if (r->nesting > 0 && rfifolock_is_locked(r)) {
>>>>           r->tail--; /* put ticket back, we're nesting */
>>>>       } else {
>>>>           while (ticket != r->head) {
>>>> @@ -69,10 +69,15 @@ void rfifolock_unlock(RFifoLock *r)
>>>>   {
>>>>       qemu_mutex_lock(&r->lock);
>>>>       assert(r->nesting > 0);
>>>> -    assert(qemu_thread_is_self(&r->owner_thread));
>>>> +    assert(rfifolock_is_locked(r));
>>>>       if (--r->nesting == 0) {
>>>>           r->head++;
>>>>           qemu_cond_broadcast(&r->cond);
>>>>       }
>>>>       qemu_mutex_unlock(&r->lock);
>>>>   }
>>>> +
>>>> +int rfifolock_is_locked(RFifoLock *r)
>>>> +{
>>>> +    return qemu_thread_is_self(&r->owner_thread);
>>>> +}
>>> The function name confused me since "does the current thread hold the
>>> lock?" != "does anyone currently hold the lock?".
>>>
>>> I suggest:
>>>
>>> bool rfifolock_held_by_current_thread(RFifoLock *r) {
>>>      return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
>>> }
>>>
>>> Then the r->nesting > 0 testing can also be dropped by callers, which is
>>> good since rfifolock_is_locked() does not return a meaningful result
>>> when r->nesting == 0.
>> actually the function is broken in the current state:
>>      aio_context_acquire()
>>      aio_context_release()
>>      aio_context_is_locked() will return true.
>> the problem is that owner thread is not reset on lock release.
> The owner thread field is only valid when nesting > 0.
>
>> with your proposal the function become racy if called from outer
>> thread. I need to think a bit here.
> This is thread-safe:
>
> bool owner;
>
> qemu_mutex_lock(&r->lock);
> owner = r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
> qemu_mutex_unlock(&r->lock);
>
> return owner;
yep, I know.

But I do not want to take the lock for check.
IMHO it would be better to

@@ -68,11 +68,16 @@ void rfifolock_lock(RFifoLock *r)
  void rfifolock_unlock(RFifoLock *r)
  {
      qemu_mutex_lock(&r->lock);
-    assert(r->nesting > 0);
-    assert(qemu_thread_is_self(&r->owner_thread));
+    assert(rfifolock_is_owner(r));
      if (--r->nesting == 0) {
+        qemu_thread_clear(&r->owner_thread);
          r->head++;
          qemu_cond_broadcast(&r->cond);
      }
      qemu_mutex_unlock(&r->lock);
  }
+
+bool rfifolock_is_owner(RFifoLock *r)
+{
+    return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
+}

which does not require lock and thread safe.

Den
Paolo Bonzini Nov. 2, 2015, 1:55 p.m. UTC | #7
On 02/11/2015 14:39, Denis V. Lunev wrote:
>> This is thread-safe:
>>
>> bool owner;
>>
>> qemu_mutex_lock(&r->lock);
>> owner = r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
>> qemu_mutex_unlock(&r->lock);
>>
>> return owner;
> yep, I know.
> 
> But I do not want to take the lock for check.

You can use a trylock.  If it fails, it is definitely safe to return false.

> IMHO it would be better to
> 
> @@ -68,11 +68,16 @@ void rfifolock_lock(RFifoLock *r)
>  void rfifolock_unlock(RFifoLock *r)
>  {
>      qemu_mutex_lock(&r->lock);
> -    assert(r->nesting > 0);
> -    assert(qemu_thread_is_self(&r->owner_thread));
> +    assert(rfifolock_is_owner(r));
>      if (--r->nesting == 0) {
> +        qemu_thread_clear(&r->owner_thread);
>          r->head++;
>          qemu_cond_broadcast(&r->cond);
>      }
>      qemu_mutex_unlock(&r->lock);
>  }
> +
> +bool rfifolock_is_owner(RFifoLock *r)
> +{
> +    return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
> +}
> 
> which does not require lock and thread safe.

I think it requires memory barriers though.  But it can be simplified:
if you clear owner_thread, you do not need to check r->nesting in
rfifolock_is_owner.

Clearing owner_thread can be done with a simple memset.

Paolo
Denis V. Lunev Nov. 2, 2015, 2:02 p.m. UTC | #8
On 11/02/2015 04:55 PM, Paolo Bonzini wrote:
>
> On 02/11/2015 14:39, Denis V. Lunev wrote:
>>> This is thread-safe:
>>>
>>> bool owner;
>>>
>>> qemu_mutex_lock(&r->lock);
>>> owner = r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
>>> qemu_mutex_unlock(&r->lock);
>>>
>>> return owner;
>> yep, I know.
>>
>> But I do not want to take the lock for check.
> You can use a trylock.  If it fails, it is definitely safe to return false.
>
>> IMHO it would be better to
>>
>> @@ -68,11 +68,16 @@ void rfifolock_lock(RFifoLock *r)
>>   void rfifolock_unlock(RFifoLock *r)
>>   {
>>       qemu_mutex_lock(&r->lock);
>> -    assert(r->nesting > 0);
>> -    assert(qemu_thread_is_self(&r->owner_thread));
>> +    assert(rfifolock_is_owner(r));
>>       if (--r->nesting == 0) {
>> +        qemu_thread_clear(&r->owner_thread);
>>           r->head++;
>>           qemu_cond_broadcast(&r->cond);
>>       }
>>       qemu_mutex_unlock(&r->lock);
>>   }
>> +
>> +bool rfifolock_is_owner(RFifoLock *r)
>> +{
>> +    return r->nesting > 0 && qemu_thread_is_self(&r->owner_thread);
>> +}
>>
>> which does not require lock and thread safe.
> I think it requires memory barriers though.  But it can be simplified:
> if you clear owner_thread, you do not need to check r->nesting in
> rfifolock_is_owner.
>
> Clearing owner_thread can be done with a simple memset.
this does not require memory barrier as call to qemu_call_broadcast
will do the job just fine.

The check for ownership is actually enough as:
- current thread could be set in the current thread only and
   cleared in the same current thread only. This is not racy at all :)
- count check is moved here for convenience only by request of
   Stefan, it is not required at all to make a decision with after
   clearing the thread.

OK, I can use memset for sure if it will be accepted :)
This was my first opinion.

Den
diff mbox

Patch

diff --git a/include/qemu/rfifolock.h b/include/qemu/rfifolock.h
index b23ab53..2c00c4b 100644
--- a/include/qemu/rfifolock.h
+++ b/include/qemu/rfifolock.h
@@ -50,5 +50,6 @@  void rfifolock_init(RFifoLock *r, void (*cb)(void *), void *opaque);
 void rfifolock_destroy(RFifoLock *r);
 void rfifolock_lock(RFifoLock *r);
 void rfifolock_unlock(RFifoLock *r);
+int  rfifolock_is_locked(RFifoLock *r);
 
 #endif /* QEMU_RFIFOLOCK_H */
diff --git a/util/rfifolock.c b/util/rfifolock.c
index afbf748..8ac58cb 100644
--- a/util/rfifolock.c
+++ b/util/rfifolock.c
@@ -48,7 +48,7 @@  void rfifolock_lock(RFifoLock *r)
     /* Take a ticket */
     unsigned int ticket = r->tail++;
 
-    if (r->nesting > 0 && qemu_thread_is_self(&r->owner_thread)) {
+    if (r->nesting > 0 && rfifolock_is_locked(r)) {
         r->tail--; /* put ticket back, we're nesting */
     } else {
         while (ticket != r->head) {
@@ -69,10 +69,15 @@  void rfifolock_unlock(RFifoLock *r)
 {
     qemu_mutex_lock(&r->lock);
     assert(r->nesting > 0);
-    assert(qemu_thread_is_self(&r->owner_thread));
+    assert(rfifolock_is_locked(r));
     if (--r->nesting == 0) {
         r->head++;
         qemu_cond_broadcast(&r->cond);
     }
     qemu_mutex_unlock(&r->lock);
 }
+
+int rfifolock_is_locked(RFifoLock *r)
+{
+    return qemu_thread_is_self(&r->owner_thread);
+}