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
5package sync
6
7import (
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
37type 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.
48func 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()
67func (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.
82func (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.
91func (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.
97type copyChecker uintptr
98
99func (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.
118type noCopy struct{}
119
120// Lock is a no-op used by -copylocks checker from `go vet`.
121func (*noCopy) Lock()   {}
122func (*noCopy) Unlock() {}
123