Source file src/sync/cond.go

     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 must not be copied after first use.
    21  //
    22  // In the terminology of the Go memory model, Cond arranges that
    23  // a call to Broadcast or Signal “synchronizes before” any Wait call
    24  // that it unblocks.
    25  //
    26  // For many simple use cases, users will be better off using channels than a
    27  // Cond (Broadcast corresponds to closing a channel, and Signal corresponds to
    28  // sending on a channel).
    29  //
    30  // For more on replacements for sync.Cond, see [Roberto Clapis's series on
    31  // advanced concurrency patterns], as well as [Bryan Mills's talk on concurrency
    32  // patterns].
    33  //
    34  // [Roberto Clapis's series on advanced concurrency patterns]: https://blogtitle.github.io/categories/concurrency/
    35  // [Bryan Mills's talk on concurrency patterns]: https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view
    36  type Cond struct {
    37  	noCopy noCopy
    38  
    39  	// L is held while observing or changing the condition
    40  	L Locker
    41  
    42  	notify  notifyList
    43  	checker copyChecker
    44  }
    45  
    46  // NewCond returns a new Cond with Locker l.
    47  func NewCond(l Locker) *Cond {
    48  	return &Cond{L: l}
    49  }
    50  
    51  // Wait atomically unlocks c.L and suspends execution
    52  // of the calling goroutine. After later resuming execution,
    53  // Wait locks c.L before returning. Unlike in other systems,
    54  // Wait cannot return unless awoken by Broadcast or Signal.
    55  //
    56  // Because c.L is not locked while Wait is waiting, the caller
    57  // typically cannot assume that the condition is true when
    58  // Wait returns. Instead, the caller should Wait in a loop:
    59  //
    60  //	c.L.Lock()
    61  //	for !condition() {
    62  //	    c.Wait()
    63  //	}
    64  //	... make use of condition ...
    65  //	c.L.Unlock()
    66  func (c *Cond) Wait() {
    67  	c.checker.check()
    68  	t := runtime_notifyListAdd(&c.notify)
    69  	c.L.Unlock()
    70  	runtime_notifyListWait(&c.notify, t)
    71  	c.L.Lock()
    72  }
    73  
    74  // Signal wakes one goroutine waiting on c, if there is any.
    75  //
    76  // It is allowed but not required for the caller to hold c.L
    77  // during the call.
    78  //
    79  // Signal() does not affect goroutine scheduling priority; if other goroutines
    80  // are attempting to lock c.L, they may be awoken before a "waiting" goroutine.
    81  func (c *Cond) Signal() {
    82  	c.checker.check()
    83  	runtime_notifyListNotifyOne(&c.notify)
    84  }
    85  
    86  // Broadcast wakes all goroutines waiting on c.
    87  //
    88  // It is allowed but not required for the caller to hold c.L
    89  // during the call.
    90  func (c *Cond) Broadcast() {
    91  	c.checker.check()
    92  	runtime_notifyListNotifyAll(&c.notify)
    93  }
    94  
    95  // copyChecker holds back pointer to itself to detect object copying.
    96  type copyChecker uintptr
    97  
    98  func (c *copyChecker) check() {
    99  	// Check if c has been copied in three steps:
   100  	// 1. The first comparison is the fast-path. If c has been initialized and not copied, this will return immediately. Otherwise, c is either not initialized, or has been copied.
   101  	// 2. Ensure c is initialized. If the CAS succeeds, we're done. If it fails, c was either initialized concurrently and we simply lost the race, or c has been copied.
   102  	// 3. Do step 1 again. Now that c is definitely initialized, if this fails, c was copied.
   103  	if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
   104  		!atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
   105  		uintptr(*c) != uintptr(unsafe.Pointer(c)) {
   106  		panic("sync.Cond is copied")
   107  	}
   108  }
   109  
   110  // noCopy may be added to structs which must not be copied
   111  // after the first use.
   112  //
   113  // See https://golang.org/issues/8005#issuecomment-190753527
   114  // for details.
   115  //
   116  // Note that it must not be embedded, due to the Lock and Unlock methods.
   117  type noCopy struct{}
   118  
   119  // Lock is a no-op used by -copylocks checker from `go vet`.
   120  func (*noCopy) Lock()   {}
   121  func (*noCopy) Unlock() {}
   122  

View as plain text