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

View as plain text