go.temporal.io/server@v1.23.0/common/locks/condition_variable_impl.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package locks 26 27 import ( 28 "sync" 29 ) 30 31 type ( 32 ConditionVariableImpl struct { 33 lock Locker 34 35 chanLock sync.Mutex 36 channel chan struct{} 37 } 38 ) 39 40 var _ ConditionVariable = (*ConditionVariableImpl)(nil) 41 42 func NewConditionVariable( 43 lock Locker, 44 ) *ConditionVariableImpl { 45 return &ConditionVariableImpl{ 46 lock: lock, 47 48 chanLock: sync.Mutex{}, 49 channel: newCVChannel(), 50 } 51 } 52 53 // Signal wakes one goroutine waiting on this condition variable, if there is any. 54 func (c *ConditionVariableImpl) Signal() { 55 c.chanLock.Lock() 56 defer c.chanLock.Unlock() 57 58 select { 59 case c.channel <- struct{}{}: 60 default: 61 //noop 62 } 63 } 64 65 // Broadcast wakes all goroutines waiting on this condition variable. 66 func (c *ConditionVariableImpl) Broadcast() { 67 newChannel := newCVChannel() 68 69 c.chanLock.Lock() 70 defer c.chanLock.Unlock() 71 72 close(c.channel) 73 c.channel = newChannel 74 } 75 76 // Wait atomically unlocks user provided lock and suspends execution of the calling goroutine. 77 // After later resuming execution, Wait locks c.L before returning. 78 // Wait can be awoken by Broadcast, Signal or user provided interrupt channel. 79 func (c *ConditionVariableImpl) Wait( 80 interrupt <-chan struct{}, 81 ) { 82 83 c.chanLock.Lock() 84 channel := c.channel 85 c.chanLock.Unlock() 86 87 // user provided lock must be released after getting the above channel 88 // so channel to be waited on < release of user lock < acquire of user lock < signal / broadcast 89 // ``` 90 // That is, if another thread is able to acquire the mutex after the about-to-block 91 // thread has released it, then a subsequent call to pthread_cond_signal() or 92 // pthread_cond_broadcast() in that thread behaves as if it were issued after the 93 // about-to-block thread has blocked. 94 // ref: https://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_cond_wait.html 95 // ``` 96 97 c.lock.Unlock() 98 defer c.lock.Lock() 99 100 select { 101 case <-channel: 102 // received signal 103 case <-interrupt: 104 // interrupted 105 } 106 } 107 108 func newCVChannel() chan struct{} { 109 return make(chan struct{}, 1) 110 }