github.com/cbeuw/gotfo@v0.0.0-20180331191851-f2b091af84de/fd_mutex.go (about) 1 // Copyright 2013 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 5 // Note fd_mutex in go1.8 and go1.9 are nearly identical, 6 // but go1.8's error messages all begin with "net: ..." 7 8 package gotfo 9 10 import ( 11 "sync/atomic" 12 ) 13 14 // fdMutex is a specialized synchronization primitive that manages 15 // lifetime of an fd and serializes access to Read, Write and Close 16 // methods on netFD. 17 type fdMutex struct { 18 state uint64 19 rsema uint32 20 wsema uint32 21 } 22 23 // fdMutex.state is organized as follows: 24 // 1 bit - whether netFD is closed, if set all subsequent lock operations will fail. 25 // 1 bit - lock for read operations. 26 // 1 bit - lock for write operations. 27 // 20 bits - total number of references (read+write+misc). 28 // 20 bits - number of outstanding read waiters. 29 // 20 bits - number of outstanding write waiters. 30 const ( 31 mutexClosed = 1 << 0 32 mutexRLock = 1 << 1 33 mutexWLock = 1 << 2 34 mutexRef = 1 << 3 35 mutexRefMask = (1<<20 - 1) << 3 36 mutexRWait = 1 << 23 37 mutexRMask = (1<<20 - 1) << 23 38 mutexWWait = 1 << 43 39 mutexWMask = (1<<20 - 1) << 43 40 ) 41 42 // Read operations must do rwlock(true)/rwunlock(true). 43 // 44 // Write operations must do rwlock(false)/rwunlock(false). 45 // 46 // Misc operations must do incref/decref. 47 // Misc operations include functions like setsockopt and setDeadline. 48 // They need to use incref/decref to ensure that they operate on the 49 // correct fd in presence of a concurrent close call (otherwise fd can 50 // be closed under their feet). 51 // 52 // Close operations must do increfAndClose/decref. 53 54 // incref adds a reference to mu. 55 // It reports whether mu is available for reading or writing. 56 func (mu *fdMutex) incref() bool { 57 for { 58 old := atomic.LoadUint64(&mu.state) 59 if old&mutexClosed != 0 { 60 return false 61 } 62 new := old + mutexRef 63 if new&mutexRefMask == 0 { 64 panic("net: inconsistent fdMutex") 65 } 66 if atomic.CompareAndSwapUint64(&mu.state, old, new) { 67 return true 68 } 69 } 70 } 71 72 // increfAndClose sets the state of mu to closed. 73 // It reports whether there is no remaining reference. 74 func (mu *fdMutex) increfAndClose() bool { 75 for { 76 old := atomic.LoadUint64(&mu.state) 77 if old&mutexClosed != 0 { 78 return false 79 } 80 // Mark as closed and acquire a reference. 81 new := (old | mutexClosed) + mutexRef 82 if new&mutexRefMask == 0 { 83 panic("net: inconsistent fdMutex") 84 } 85 // Remove all read and write waiters. 86 new &^= mutexRMask | mutexWMask 87 if atomic.CompareAndSwapUint64(&mu.state, old, new) { 88 // Wake all read and write waiters, 89 // they will observe closed flag after wakeup. 90 for old&mutexRMask != 0 { 91 old -= mutexRWait 92 runtime_Semrelease(&mu.rsema) 93 } 94 for old&mutexWMask != 0 { 95 old -= mutexWWait 96 runtime_Semrelease(&mu.wsema) 97 } 98 return true 99 } 100 } 101 } 102 103 // decref removes a reference from mu. 104 // It reports whether there is no remaining reference. 105 func (mu *fdMutex) decref() bool { 106 for { 107 old := atomic.LoadUint64(&mu.state) 108 if old&mutexRefMask == 0 { 109 panic("net: inconsistent fdMutex") 110 } 111 new := old - mutexRef 112 if atomic.CompareAndSwapUint64(&mu.state, old, new) { 113 return new&(mutexClosed|mutexRefMask) == mutexClosed 114 } 115 } 116 } 117 118 // lock adds a reference to mu and locks mu. 119 // It reports whether mu is available for reading or writing. 120 func (mu *fdMutex) rwlock(read bool) bool { 121 var mutexBit, mutexWait, mutexMask uint64 122 var mutexSema *uint32 123 if read { 124 mutexBit = mutexRLock 125 mutexWait = mutexRWait 126 mutexMask = mutexRMask 127 mutexSema = &mu.rsema 128 } else { 129 mutexBit = mutexWLock 130 mutexWait = mutexWWait 131 mutexMask = mutexWMask 132 mutexSema = &mu.wsema 133 } 134 for { 135 old := atomic.LoadUint64(&mu.state) 136 if old&mutexClosed != 0 { 137 return false 138 } 139 var new uint64 140 if old&mutexBit == 0 { 141 // Lock is free, acquire it. 142 new = (old | mutexBit) + mutexRef 143 if new&mutexRefMask == 0 { 144 panic("net: inconsistent fdMutex") 145 } 146 } else { 147 // Wait for lock. 148 new = old + mutexWait 149 if new&mutexMask == 0 { 150 panic("net: inconsistent fdMutex") 151 } 152 } 153 if atomic.CompareAndSwapUint64(&mu.state, old, new) { 154 if old&mutexBit == 0 { 155 return true 156 } 157 runtime_Semacquire(mutexSema) 158 // The signaller has subtracted mutexWait. 159 } 160 } 161 } 162 163 // unlock removes a reference from mu and unlocks mu. 164 // It reports whether there is no remaining reference. 165 func (mu *fdMutex) rwunlock(read bool) bool { 166 var mutexBit, mutexWait, mutexMask uint64 167 var mutexSema *uint32 168 if read { 169 mutexBit = mutexRLock 170 mutexWait = mutexRWait 171 mutexMask = mutexRMask 172 mutexSema = &mu.rsema 173 } else { 174 mutexBit = mutexWLock 175 mutexWait = mutexWWait 176 mutexMask = mutexWMask 177 mutexSema = &mu.wsema 178 } 179 for { 180 old := atomic.LoadUint64(&mu.state) 181 if old&mutexBit == 0 || old&mutexRefMask == 0 { 182 panic("net: inconsistent fdMutex") 183 } 184 // Drop lock, drop reference and wake read waiter if present. 185 new := (old &^ mutexBit) - mutexRef 186 if old&mutexMask != 0 { 187 new -= mutexWait 188 } 189 if atomic.CompareAndSwapUint64(&mu.state, old, new) { 190 if old&mutexMask != 0 { 191 runtime_Semrelease(mutexSema) 192 } 193 return new&(mutexClosed|mutexRefMask) == mutexClosed 194 } 195 } 196 } 197 198 // incref adds a reference to fd. 199 // It returns an error when fd cannot be used. 200 func (fd *netFD) incref() error { 201 if !fd.fdmu.incref() { 202 return errClosing 203 } 204 return nil 205 } 206 207 // decref removes a reference from fd. 208 // It also closes fd when the state of fd is set to closed and there 209 // is no remaining reference. 210 func (fd *netFD) decref() error { 211 if fd.fdmu.decref() { 212 return fd.destroy() 213 } 214 215 return nil 216 } 217 218 // readLock adds a reference to fd and locks fd for reading. 219 // It returns an error when fd cannot be used for reading. 220 func (fd *netFD) readLock() error { 221 if !fd.fdmu.rwlock(true) { 222 return errClosing 223 } 224 return nil 225 } 226 227 // readUnlock removes a reference from fd and unlocks fd for reading. 228 // It also closes fd when the state of fd is set to closed and there 229 // is no remaining reference. 230 func (fd *netFD) readUnlock() { 231 if fd.fdmu.rwunlock(true) { 232 fd.destroy() 233 } 234 } 235 236 // writeLock adds a reference to fd and locks fd for writing. 237 // It returns an error when fd cannot be used for writing. 238 func (fd *netFD) writeLock() error { 239 if !fd.fdmu.rwlock(false) { 240 return errClosing 241 } 242 return nil 243 } 244 245 // writeUnlock removes a reference from fd and unlocks fd for writing. 246 // It also closes fd when the state of fd is set to closed and there 247 // is no remaining reference. 248 func (fd *netFD) writeUnlock() { 249 if fd.fdmu.rwunlock(false) { 250 fd.destroy() 251 } 252 }