github.com/searKing/golang/go@v1.2.117/sync/conditional_variable.go (about) 1 // Copyright 2021 The searKing Author. 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 "context" 9 "errors" 10 "sync" 11 "time" 12 ) 13 14 // ConditionVariable is an object able to block the calling thread until notified to resume. 15 // see http://www.cplusplus.com/reference/condition_variable/condition_variable/ 16 type ConditionVariable struct { 17 subject Subject 18 } 19 20 // Wait atomically unlocks c.L and suspends execution 21 // of the calling goroutine. After later resuming execution, 22 // Wait locks c.L before returning. Unlike in other systems, 23 // Wait cannot return unless awoken by Broadcast or Signal. 24 // 25 // Because c.L is not locked when Wait first resumes, the caller 26 // typically cannot assume that the condition is true when 27 // Wait returns. Instead, the caller should Wait in a loop: 28 // 29 // c.L.Lock() 30 // for !condition() { 31 // c.Wait() 32 // } 33 // ... make use of condition ... 34 // c.L.Unlock() 35 // 36 // Wait wait until notified 37 func (c *ConditionVariable) Wait(lck sync.Locker) { 38 _ = c.WaitContext(context.Background(), lck) 39 } 40 41 // WaitPred wait until notified 42 // If pred is specified (2), the function only blocks if pred returns false, 43 // and notifications can only unblock the thread when it becomes true 44 // (which is specially useful to check against spurious wake-up calls). This version (2) behaves as if implemented as: 45 // while (!pred()) wait(lck); 46 func (c *ConditionVariable) WaitPred(lck sync.Locker, pred func() bool) { 47 _ = c.WaitPredContext(context.Background(), lck, pred) 48 } 49 50 // WaitFor The execution of the current goroutine (which shall have locked lck's mutex) is blocked during rel_time, 51 // or until notified (if the latter happens first). 52 // WaitFor wait for timeout or until notified 53 // It behaves as if implemented as: 54 // return wait_until (lck, chrono::steady_clock::now() + rel_time); 55 func (c *ConditionVariable) WaitFor(lck sync.Locker, timeout time.Duration) error { 56 return c.WaitUntil(lck, time.Now().Add(timeout)) 57 } 58 59 // WaitForPred wait for timeout or until notified 60 // If pred is nil, do as pred returns false always, 61 // If pred is specified, the function only blocks if pred returns false, 62 // and notifications can only unblock the thread when it becomes true 63 // (which is especially useful to check against spurious wake-up calls). 64 // It behaves as if implemented as: 65 // return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred)); 66 func (c *ConditionVariable) WaitForPred(lck sync.Locker, timeout time.Duration, pred func() bool) bool { 67 return c.WaitUntilPred(lck, time.Now().Add(timeout), pred) 68 } 69 70 // WaitUntil wait until notified or time point 71 // The execution of the current thread (which shall have locked lck's mutex) is blocked either 72 // until notified or until abs_time, whichever happens first. 73 func (c *ConditionVariable) WaitUntil(lck sync.Locker, d time.Time) error { 74 ctx, cancelCtx := context.WithDeadline(context.Background(), d) 75 defer cancelCtx() 76 return c.WaitContext(ctx, lck) 77 } 78 79 // WaitUntilPred wait until notified or ctx done 80 // The execution of the current thread (which shall have locked lck's mutex) is blocked either 81 // until notified or until ctx, whichever happens first. 82 // If pred is nil, do as pred returns false always, 83 // If pred is specified, the function only blocks if pred returns false, 84 // and notifications can only unblock the thread when it becomes true 85 // (which is especially useful to check against spurious wake-up calls). 86 // It behaves as if implemented as: 87 // while (!pred()) 88 // 89 // if ( wait_until(lck,abs_time) == cv_status::timeout) 90 // return pred(); 91 // 92 // return true; 93 func (c *ConditionVariable) WaitUntilPred(lck sync.Locker, d time.Time, pred func() bool) bool { 94 ctx, cancelCtx := context.WithDeadline(context.Background(), d) 95 defer cancelCtx() 96 return c.WaitPredContext(ctx, lck, pred) 97 } 98 99 // WaitContext wait until notified or time point 100 // The execution of the current thread (which shall have locked lck's mutex) is blocked either 101 // until notified or until ctx done, whichever happens first. 102 func (c *ConditionVariable) WaitContext(ctx context.Context, lck sync.Locker) error { 103 eventC, cancel := c.subject.Subscribe() 104 defer cancel() 105 lck.Unlock() 106 defer lck.Lock() 107 108 select { 109 case <-eventC: 110 return nil 111 case <-ctx.Done(): 112 return ctx.Err() 113 } 114 } 115 116 // WaitPredContext wait until notified or ctx done 117 // The execution of the current thread (which shall have locked lck's mutex) is blocked either 118 // until notified or until ctx, whichever happens first. 119 // If pred is nil, do as pred returns false always, 120 // If pred is specified, the function only blocks if pred returns false, 121 // and notifications can only unblock the thread when it becomes true 122 // (which is especially useful to check against spurious wake-up calls). 123 // It behaves as if implemented as: 124 // while (!pred()) 125 // 126 // if ( wait_until(ctx,lck) == cv_status::timeout) 127 // return pred(); 128 // 129 // return true; 130 func (c *ConditionVariable) WaitPredContext(ctx context.Context, lck sync.Locker, pred func() bool) bool { 131 if pred == nil { 132 pred = func() bool { return false } 133 } 134 for !pred() { 135 if errors.Is(c.WaitContext(ctx, lck), context.DeadlineExceeded) { 136 return pred() 137 } 138 } 139 return true 140 } 141 142 // Signal wakes one goroutine waiting on c, if there is any. 143 // 144 // It is allowed but not required for the caller to hold c.L 145 // during the call. 146 func (c *ConditionVariable) Signal() { 147 go func() { 148 _ = c.subject.PublishSignal(context.Background(), nil) 149 }() 150 } 151 152 // Broadcast wakes all goroutines waiting on c. 153 // 154 // It is allowed but not required for the caller to hold c.L 155 // during the call. 156 func (c *ConditionVariable) Broadcast() { 157 go func() { 158 _ = c.subject.PublishBroadcast(context.Background(), nil) 159 }() 160 }