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