]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
lib/lock: code barriers to deal with spurious wakeups
authorVMware, Inc <>
Thu, 18 Nov 2010 22:53:14 +0000 (14:53 -0800)
committerMarcelo Vanzin <mvanzin@vmware.com>
Thu, 18 Nov 2010 22:53:14 +0000 (14:53 -0800)
A condVar wait may be subject to a spurious wakeup; code against
this.

Signed-off-by: Marcelo Vanzin <mvanzin@vmware.com>
open-vm-tools/lib/lock/ulBarrier.c

index 38c09ed0659aa40c06fc97fdfed2658400783bf2..65f67f54892ad57ec6a93c61411e1dc65886c7e6 100644 (file)
@@ -36,8 +36,8 @@ struct MXUserBarrier
 {
    MXUserHeader     header;        // Barrier's ID information
    MXUserExclLock  *lock;          // Barrier's (internal) lock
-   uint32           configCount;   // Hold until this many threads arrive.
-   uint32           curContext;    // Normal arrivals go to this context
+   uint32           configCount;   // Hold until this many threads arrive
+   volatile uint32  curContext;    // Arrivals go to this context
    BarrierContext   contexts[2];   // The normal and abnormal contexts
 };
 
@@ -238,18 +238,21 @@ void
 MXUser_EnterBarrier(MXUserBarrier *barrier)  // IN/OUT:
 {
    BarrierContext *ptr;
+   uint32 context;
 
    ASSERT(barrier && (barrier->header.signature == MXUSER_BARRIER_SIGNATURE));
 
    MXUser_AcquireExclLock(barrier->lock);
 
-   ptr = &barrier->contexts[barrier->curContext];
+   context = barrier->curContext;
+   ptr = &barrier->contexts[context];
 
    ptr->count++;
 
    if (ptr->count == barrier->configCount) {
-      /* The last thread has entered; release the other threads */
       /*
+       * The last thread has entered; release the other threads
+       *
        * Flip the current context. Should a thread leave the barrier and
        * enter the barrier while the barrier is "emptying" the thread will
        * parked on the condVar that is not "emptying". Eventually everything
@@ -257,14 +260,20 @@ MXUser_EnterBarrier(MXUserBarrier *barrier)  // IN/OUT:
        * context's condVar.
        */
 
-      barrier->curContext = (barrier->curContext + 1) & 0x1;
+      barrier->curContext = (context + 1) & 0x1;
       ASSERT(barrier->contexts[barrier->curContext].count == 0);
 
       /* Wake up all of the waiting threads. */
       MXUser_BroadcastCondVar(ptr->condVar);
    } else {
-      /* Not the last thread in... sleep until the last thread appears */
-      MXUser_WaitCondVarExclLock(barrier->lock, ptr->condVar);
+      /*
+       * Not the last thread in... wait/sleep until the last thread appears.
+       * Protect against spurious wakeups.
+       */
+
+      while (barrier->curContext == context) {
+         MXUser_WaitCondVarExclLock(barrier->lock, ptr->condVar);
+      }
    }
 
    ptr->count--;