Dmitriy Vyukov | 23e15f7 | 2013-08-09 21:43:00 +0400 | [diff] [blame] | 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 | package net |
| 6 | |
| 7 | import "sync/atomic" |
| 8 | |
| 9 | // fdMutex is a specialized synchronization primitive |
| 10 | // that manages lifetime of an fd and serializes access |
| 11 | // to Read and Write methods on netFD. |
| 12 | type fdMutex struct { |
| 13 | state uint64 |
| 14 | rsema uint32 |
| 15 | wsema uint32 |
| 16 | } |
| 17 | |
| 18 | // fdMutex.state is organized as follows: |
| 19 | // 1 bit - whether netFD is closed, if set all subsequent lock operations will fail. |
| 20 | // 1 bit - lock for read operations. |
| 21 | // 1 bit - lock for write operations. |
| 22 | // 20 bits - total number of references (read+write+misc). |
| 23 | // 20 bits - number of outstanding read waiters. |
| 24 | // 20 bits - number of outstanding write waiters. |
| 25 | const ( |
| 26 | mutexClosed = 1 << 0 |
| 27 | mutexRLock = 1 << 1 |
| 28 | mutexWLock = 1 << 2 |
| 29 | mutexRef = 1 << 3 |
| 30 | mutexRefMask = (1<<20 - 1) << 3 |
| 31 | mutexRWait = 1 << 23 |
| 32 | mutexRMask = (1<<20 - 1) << 23 |
| 33 | mutexWWait = 1 << 43 |
| 34 | mutexWMask = (1<<20 - 1) << 43 |
| 35 | ) |
| 36 | |
| 37 | // Read operations must do RWLock(true)/RWUnlock(true). |
| 38 | // Write operations must do RWLock(false)/RWUnlock(false). |
| 39 | // Misc operations must do Incref/Decref. Misc operations include functions like |
| 40 | // setsockopt and setDeadline. They need to use Incref/Decref to ensure that |
| 41 | // they operate on the correct fd in presence of a concurrent Close call |
| 42 | // (otherwise fd can be closed under their feet). |
| 43 | // Close operation must do IncrefAndClose/Decref. |
| 44 | |
| 45 | // RWLock/Incref return whether fd is open. |
| 46 | // RWUnlock/Decref return whether fd is closed and there are no remaining references. |
| 47 | |
| 48 | func (mu *fdMutex) Incref() bool { |
| 49 | for { |
| 50 | old := atomic.LoadUint64(&mu.state) |
| 51 | if old&mutexClosed != 0 { |
| 52 | return false |
| 53 | } |
| 54 | new := old + mutexRef |
| 55 | if new&mutexRefMask == 0 { |
| 56 | panic("net: inconsistent fdMutex") |
| 57 | } |
| 58 | if atomic.CompareAndSwapUint64(&mu.state, old, new) { |
| 59 | return true |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | func (mu *fdMutex) IncrefAndClose() bool { |
| 65 | for { |
| 66 | old := atomic.LoadUint64(&mu.state) |
| 67 | if old&mutexClosed != 0 { |
| 68 | return false |
| 69 | } |
| 70 | // Mark as closed and acquire a reference. |
| 71 | new := (old | mutexClosed) + mutexRef |
| 72 | if new&mutexRefMask == 0 { |
| 73 | panic("net: inconsistent fdMutex") |
| 74 | } |
| 75 | // Remove all read and write waiters. |
| 76 | new &^= mutexRMask | mutexWMask |
| 77 | if atomic.CompareAndSwapUint64(&mu.state, old, new) { |
| 78 | // Wake all read and write waiters, |
| 79 | // they will observe closed flag after wakeup. |
| 80 | for old&mutexRMask != 0 { |
| 81 | old -= mutexRWait |
| 82 | runtime_Semrelease(&mu.rsema) |
| 83 | } |
| 84 | for old&mutexWMask != 0 { |
| 85 | old -= mutexWWait |
| 86 | runtime_Semrelease(&mu.wsema) |
| 87 | } |
| 88 | return true |
| 89 | } |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | func (mu *fdMutex) Decref() bool { |
| 94 | for { |
| 95 | old := atomic.LoadUint64(&mu.state) |
| 96 | if old&mutexRefMask == 0 { |
| 97 | panic("net: inconsistent fdMutex") |
| 98 | } |
| 99 | new := old - mutexRef |
| 100 | if atomic.CompareAndSwapUint64(&mu.state, old, new) { |
Dmitriy Vyukov | 727dd08 | 2013-08-16 16:02:55 +0400 | [diff] [blame] | 101 | return new&(mutexClosed|mutexRefMask) == mutexClosed |
Dmitriy Vyukov | 23e15f7 | 2013-08-09 21:43:00 +0400 | [diff] [blame] | 102 | } |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | func (mu *fdMutex) RWLock(read bool) bool { |
| 107 | var mutexBit, mutexWait, mutexMask uint64 |
| 108 | var mutexSema *uint32 |
| 109 | if read { |
| 110 | mutexBit = mutexRLock |
| 111 | mutexWait = mutexRWait |
| 112 | mutexMask = mutexRMask |
| 113 | mutexSema = &mu.rsema |
| 114 | } else { |
| 115 | mutexBit = mutexWLock |
| 116 | mutexWait = mutexWWait |
| 117 | mutexMask = mutexWMask |
| 118 | mutexSema = &mu.wsema |
| 119 | } |
| 120 | for { |
| 121 | old := atomic.LoadUint64(&mu.state) |
| 122 | if old&mutexClosed != 0 { |
| 123 | return false |
| 124 | } |
| 125 | var new uint64 |
| 126 | if old&mutexBit == 0 { |
| 127 | // Lock is free, acquire it. |
| 128 | new = (old | mutexBit) + mutexRef |
| 129 | if new&mutexRefMask == 0 { |
| 130 | panic("net: inconsistent fdMutex") |
| 131 | } |
| 132 | } else { |
| 133 | // Wait for lock. |
| 134 | new = old + mutexWait |
| 135 | if new&mutexMask == 0 { |
| 136 | panic("net: inconsistent fdMutex") |
| 137 | } |
| 138 | } |
| 139 | if atomic.CompareAndSwapUint64(&mu.state, old, new) { |
| 140 | if old&mutexBit == 0 { |
| 141 | return true |
| 142 | } |
| 143 | runtime_Semacquire(mutexSema) |
| 144 | // The signaller has subtracted mutexWait. |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | func (mu *fdMutex) RWUnlock(read bool) bool { |
| 150 | var mutexBit, mutexWait, mutexMask uint64 |
| 151 | var mutexSema *uint32 |
| 152 | if read { |
| 153 | mutexBit = mutexRLock |
| 154 | mutexWait = mutexRWait |
| 155 | mutexMask = mutexRMask |
| 156 | mutexSema = &mu.rsema |
| 157 | } else { |
| 158 | mutexBit = mutexWLock |
| 159 | mutexWait = mutexWWait |
| 160 | mutexMask = mutexWMask |
| 161 | mutexSema = &mu.wsema |
| 162 | } |
| 163 | for { |
| 164 | old := atomic.LoadUint64(&mu.state) |
| 165 | if old&mutexBit == 0 || old&mutexRefMask == 0 { |
| 166 | panic("net: inconsistent fdMutex") |
| 167 | } |
| 168 | // Drop lock, drop reference and wake read waiter if present. |
| 169 | new := (old &^ mutexBit) - mutexRef |
| 170 | if old&mutexMask != 0 { |
| 171 | new -= mutexWait |
| 172 | } |
| 173 | if atomic.CompareAndSwapUint64(&mu.state, old, new) { |
| 174 | if old&mutexMask != 0 { |
| 175 | runtime_Semrelease(mutexSema) |
| 176 | } |
Dmitriy Vyukov | 727dd08 | 2013-08-16 16:02:55 +0400 | [diff] [blame] | 177 | return new&(mutexClosed|mutexRefMask) == mutexClosed |
Dmitriy Vyukov | 23e15f7 | 2013-08-09 21:43:00 +0400 | [diff] [blame] | 178 | } |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | // Implemented in runtime package. |
| 183 | func runtime_Semacquire(sema *uint32) |
| 184 | func runtime_Semrelease(sema *uint32) |