github.com/kaydxh/golang@v0.0.131/go/sync/cond.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package sync 23 24 import ( 25 "fmt" 26 "sync" 27 "sync/atomic" 28 "time" 29 "unsafe" 30 ) 31 32 // Cond 条件变量不保证顺序性,即signal只是通知wait去获取数据,wait拿到的数据是不是 33 // signal当时给的数据,不能保证 34 type Cond struct { 35 L sync.Locker 36 ch chan struct{} 37 checker copyChecker 38 noCopy noCopy 39 } 40 41 func NewCond(l sync.Locker) *Cond { 42 c := &Cond{ 43 L: l, 44 ch: make(chan struct{}), 45 } 46 return c 47 } 48 49 func (c *Cond) wait() { 50 c.checker.check() 51 c.L.Unlock() 52 defer c.L.Lock() 53 <-c.ch 54 55 } 56 57 func (c *Cond) waitFor(timeout time.Duration) error { 58 c.L.Unlock() 59 defer c.L.Lock() 60 61 select { 62 case <-c.ch: 63 return nil 64 case <-time.After(timeout): 65 return fmt.Errorf("wait timeout: %v\n", timeout) 66 } 67 68 } 69 70 func (c *Cond) WaitForDo(timeout time.Duration, pred func() bool, do func() error) error { 71 c.checker.check() 72 c.L.Lock() 73 defer c.L.Unlock() 74 75 for !pred() { 76 start := time.Now() 77 err := c.waitFor(timeout) 78 if err != nil { 79 return err 80 } 81 82 timeout -= time.Now().Sub(start) 83 } 84 if do != nil { 85 do() 86 } 87 return nil 88 } 89 90 func (c *Cond) WaitUntil(pred func() bool) { 91 c.checker.check() 92 c.WaitUntilDo(pred, func() error { return nil }) 93 } 94 95 func (c *Cond) WaitUntilDo(pred func() bool, do func() error) { 96 c.checker.check() 97 for { 98 err := c.WaitForDo(5*time.Second, pred, do) 99 if err == nil { 100 break 101 } 102 } 103 104 } 105 106 func (c *Cond) SignalDo(do func() error) { 107 c.L.Lock() 108 if do != nil { 109 do() 110 } 111 c.L.Unlock() 112 113 c.Signal() 114 } 115 116 func (c *Cond) BroadcastDo(do func() error) { 117 c.L.Lock() 118 if do != nil { 119 do() 120 } 121 c.L.Unlock() 122 123 c.Broadcast() 124 } 125 126 func (c *Cond) Signal() { 127 c.checker.check() 128 go func() { 129 select { 130 case c.ch <- struct{}{}: 131 default: 132 } 133 }() 134 } 135 136 func (c *Cond) Broadcast() { 137 c.checker.check() 138 go func() { 139 close(c.ch) 140 c.ch = make(chan struct{}) 141 }() 142 } 143 144 // copyChecker holds back pointer to itself to detect object copying. 145 type copyChecker uintptr 146 147 func (c *copyChecker) check() { 148 if uintptr(*c) != uintptr(unsafe.Pointer(c)) && 149 !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) && 150 uintptr(*c) != uintptr(unsafe.Pointer(c)) { 151 panic("sync.Cond is copied") 152 } 153 } 154 155 // noCopy may be embedded into structs which must not be copied 156 // after the first use. 157 // 158 // See https://golang.org/issues/8005#issuecomment-190753527 159 // for details. 160 type noCopy struct{} 161 162 // Lock is a no-op used by -copylocks checker from `go vet`. 163 func (*noCopy) Lock() {} 164 func (*noCopy) Unlock() {}