github.com/FlowerWrong/netstack@v0.0.0-20191009141956-e5848263af28/gate/gate.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package gate provides a usage Gate synchronization primitive. 16 package gate 17 18 import ( 19 "sync/atomic" 20 ) 21 22 const ( 23 // gateClosed is the bit set in the gate's user count to indicate that 24 // it has been closed. It is the MSB of the 32-bit field; the other 31 25 // bits carry the actual count. 26 gateClosed = 0x80000000 27 ) 28 29 // Gate is a synchronization primitive that allows concurrent goroutines to 30 // "enter" it as long as it hasn't been closed yet. Once it's been closed, 31 // goroutines cannot enter it anymore, but are allowed to leave, and the closer 32 // will be informed when all goroutines have left. 33 // 34 // Many goroutines are allowed to enter the gate concurrently, but only one is 35 // allowed to close it. 36 // 37 // This is similar to a r/w critical section, except that goroutines "entering" 38 // never block: they either enter immediately or fail to enter. The closer will 39 // block waiting for all goroutines currently inside the gate to leave. 40 // 41 // This function is implemented efficiently. On x86, only one interlocked 42 // operation is performed on enter, and one on leave. 43 // 44 // This is useful, for example, in cases when a goroutine is trying to clean up 45 // an object for which multiple goroutines have pointers. In such a case, users 46 // would be required to enter and leave the gates, and the cleaner would wait 47 // until all users are gone (and no new ones are allowed) before proceeding. 48 // 49 // Users: 50 // 51 // if !g.Enter() { 52 // // Gate is closed, we can't use the object. 53 // return 54 // } 55 // 56 // // Do something with object. 57 // [...] 58 // 59 // g.Leave() 60 // 61 // Closer: 62 // 63 // // Prevent new users from using the object, and wait for the existing 64 // // ones to complete. 65 // g.Close() 66 // 67 // // Clean up the object. 68 // [...] 69 // 70 type Gate struct { 71 userCount uint32 72 done chan struct{} 73 } 74 75 // Enter tries to enter the gate. It will succeed if it hasn't been closed yet, 76 // in which case the caller must eventually call Leave(). 77 // 78 // This function is thread-safe. 79 func (g *Gate) Enter() bool { 80 if g == nil { 81 return false 82 } 83 84 for { 85 v := atomic.LoadUint32(&g.userCount) 86 if v&gateClosed != 0 { 87 return false 88 } 89 90 if atomic.CompareAndSwapUint32(&g.userCount, v, v+1) { 91 return true 92 } 93 } 94 } 95 96 // Leave leaves the gate. This must only be called after a successful call to 97 // Enter(). If the gate has been closed and this is the last one inside the 98 // gate, it will notify the closer that the gate is done. 99 // 100 // This function is thread-safe. 101 func (g *Gate) Leave() { 102 for { 103 v := atomic.LoadUint32(&g.userCount) 104 if v&^gateClosed == 0 { 105 panic("leaving a gate with zero usage count") 106 } 107 108 if atomic.CompareAndSwapUint32(&g.userCount, v, v-1) { 109 if v == gateClosed+1 { 110 close(g.done) 111 } 112 return 113 } 114 } 115 } 116 117 // Close closes the gate for entering, and waits until all goroutines [that are 118 // currently inside the gate] leave before returning. 119 // 120 // Only one goroutine can call this function. 121 func (g *Gate) Close() { 122 for { 123 v := atomic.LoadUint32(&g.userCount) 124 if v&^gateClosed != 0 && g.done == nil { 125 g.done = make(chan struct{}) 126 } 127 if atomic.CompareAndSwapUint32(&g.userCount, v, v|gateClosed) { 128 if v&^gateClosed != 0 { 129 <-g.done 130 } 131 return 132 } 133 } 134 }