rsc.io/go@v0.0.0-20150416155037-e040fd465409/src/sync/cond.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sync
     6  
     7  import (
     8  	"sync/atomic"
     9  	"unsafe"
    10  )
    11  
    12  // Cond implements a condition variable, a rendezvous point
    13  // for goroutines waiting for or announcing the occurrence
    14  // of an event.
    15  //
    16  // Each Cond has an associated Locker L (often a *Mutex or *RWMutex),
    17  // which must be held when changing the condition and
    18  // when calling the Wait method.
    19  //
    20  // A Cond can be created as part of other structures.
    21  // A Cond must not be copied after first use.
    22  type Cond struct {
    23  	// L is held while observing or changing the condition
    24  	L Locker
    25  
    26  	sema    syncSema
    27  	waiters uint32 // number of waiters
    28  	checker copyChecker
    29  }
    30  
    31  // NewCond returns a new Cond with Locker l.
    32  func NewCond(l Locker) *Cond {
    33  	return &Cond{L: l}
    34  }
    35  
    36  // Wait atomically unlocks c.L and suspends execution
    37  // of the calling goroutine.  After later resuming execution,
    38  // Wait locks c.L before returning.  Unlike in other systems,
    39  // Wait cannot return unless awoken by Broadcast or Signal.
    40  //
    41  // Because c.L is not locked when Wait first resumes, the caller
    42  // typically cannot assume that the condition is true when
    43  // Wait returns.  Instead, the caller should Wait in a loop:
    44  //
    45  //    c.L.Lock()
    46  //    for !condition() {
    47  //        c.Wait()
    48  //    }
    49  //    ... make use of condition ...
    50  //    c.L.Unlock()
    51  //
    52  func (c *Cond) Wait() {
    53  	c.checker.check()
    54  	if raceenabled {
    55  		raceDisable()
    56  	}
    57  	atomic.AddUint32(&c.waiters, 1)
    58  	if raceenabled {
    59  		raceEnable()
    60  	}
    61  	c.L.Unlock()
    62  	runtime_Syncsemacquire(&c.sema)
    63  	c.L.Lock()
    64  }
    65  
    66  // Signal wakes one goroutine waiting on c, if there is any.
    67  //
    68  // It is allowed but not required for the caller to hold c.L
    69  // during the call.
    70  func (c *Cond) Signal() {
    71  	c.signalImpl(false)
    72  }
    73  
    74  // Broadcast wakes all goroutines waiting on c.
    75  //
    76  // It is allowed but not required for the caller to hold c.L
    77  // during the call.
    78  func (c *Cond) Broadcast() {
    79  	c.signalImpl(true)
    80  }
    81  
    82  func (c *Cond) signalImpl(all bool) {
    83  	c.checker.check()
    84  	if raceenabled {
    85  		raceDisable()
    86  	}
    87  	for {
    88  		old := atomic.LoadUint32(&c.waiters)
    89  		if old == 0 {
    90  			if raceenabled {
    91  				raceEnable()
    92  			}
    93  			return
    94  		}
    95  		new := old - 1
    96  		if all {
    97  			new = 0
    98  		}
    99  		if atomic.CompareAndSwapUint32(&c.waiters, old, new) {
   100  			if raceenabled {
   101  				raceEnable()
   102  			}
   103  			runtime_Syncsemrelease(&c.sema, old-new)
   104  			return
   105  		}
   106  	}
   107  }
   108  
   109  // copyChecker holds back pointer to itself to detect object copying.
   110  type copyChecker uintptr
   111  
   112  func (c *copyChecker) check() {
   113  	if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
   114  		!atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
   115  		uintptr(*c) != uintptr(unsafe.Pointer(c)) {
   116  		panic("sync.Cond is copied")
   117  	}
   118  }