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.
9 // fdMutex is a specialized synchronization primitive that manages
10 // lifetime of an fd and serializes access to Read, Write and Close
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.
30 mutexRefMask
= (1<<20 - 1) << 3
32 mutexRMask
= (1<<20 - 1) << 23
34 mutexWMask
= (1<<20 - 1) << 43
37 // Read operations must do rwlock(true)/rwunlock(true).
39 // Write operations must do rwlock(false)/rwunlock(false).
41 // Misc operations must do incref/decref.
42 // Misc operations include functions like setsockopt and setDeadline.
43 // They need to use incref/decref to ensure that they operate on the
44 // correct fd in presence of a concurrent close call (otherwise fd can
45 // be closed under their feet).
47 // Close operations must do increfAndClose/decref.
49 // incref adds a reference to mu.
50 // It reports whether mu is available for reading or writing.
51 func (mu
*fdMutex
) incref() bool {
53 old
:= atomic
.LoadUint64(&mu
.state
)
54 if old
&mutexClosed
!= 0 {
58 if new&mutexRefMask
== 0 {
59 panic("net: inconsistent fdMutex")
61 if atomic
.CompareAndSwapUint64(&mu
.state
, old
, new) {
67 // increfAndClose sets the state of mu to closed.
68 // It reports whether there is no remaining reference.
69 func (mu
*fdMutex
) increfAndClose() bool {
71 old
:= atomic
.LoadUint64(&mu
.state
)
72 if old
&mutexClosed
!= 0 {
75 // Mark as closed and acquire a reference.
76 new := (old | mutexClosed
) + mutexRef
77 if new&mutexRefMask
== 0 {
78 panic("net: inconsistent fdMutex")
80 // Remove all read and write waiters.
81 new &^= mutexRMask | mutexWMask
82 if atomic
.CompareAndSwapUint64(&mu
.state
, old
, new) {
83 // Wake all read and write waiters,
84 // they will observe closed flag after wakeup.
85 for old
&mutexRMask
!= 0 {
87 runtime_Semrelease(&mu
.rsema
)
89 for old
&mutexWMask
!= 0 {
91 runtime_Semrelease(&mu
.wsema
)
98 // decref removes a reference from mu.
99 // It reports whether there is no remaining reference.
100 func (mu
*fdMutex
) decref() bool {
102 old
:= atomic
.LoadUint64(&mu
.state
)
103 if old
&mutexRefMask
== 0 {
104 panic("net: inconsistent fdMutex")
106 new := old
- mutexRef
107 if atomic
.CompareAndSwapUint64(&mu
.state
, old
, new) {
108 return new&(mutexClosed|mutexRefMask
) == mutexClosed
113 // lock adds a reference to mu and locks mu.
114 // It reports whether mu is available for reading or writing.
115 func (mu
*fdMutex
) rwlock(read
bool) bool {
116 var mutexBit
, mutexWait
, mutexMask
uint64
117 var mutexSema
*uint32
119 mutexBit
= mutexRLock
120 mutexWait
= mutexRWait
121 mutexMask
= mutexRMask
122 mutexSema
= &mu
.rsema
124 mutexBit
= mutexWLock
125 mutexWait
= mutexWWait
126 mutexMask
= mutexWMask
127 mutexSema
= &mu
.wsema
130 old
:= atomic
.LoadUint64(&mu
.state
)
131 if old
&mutexClosed
!= 0 {
135 if old
&mutexBit
== 0 {
136 // Lock is free, acquire it.
137 new = (old | mutexBit
) + mutexRef
138 if new&mutexRefMask
== 0 {
139 panic("net: inconsistent fdMutex")
143 new = old
+ mutexWait
144 if new&mutexMask
== 0 {
145 panic("net: inconsistent fdMutex")
148 if atomic
.CompareAndSwapUint64(&mu
.state
, old
, new) {
149 if old
&mutexBit
== 0 {
152 runtime_Semacquire(mutexSema
)
153 // The signaller has subtracted mutexWait.
158 // unlock removes a reference from mu and unlocks mu.
159 // It reports whether there is no remaining reference.
160 func (mu
*fdMutex
) rwunlock(read
bool) bool {
161 var mutexBit
, mutexWait
, mutexMask
uint64
162 var mutexSema
*uint32
164 mutexBit
= mutexRLock
165 mutexWait
= mutexRWait
166 mutexMask
= mutexRMask
167 mutexSema
= &mu
.rsema
169 mutexBit
= mutexWLock
170 mutexWait
= mutexWWait
171 mutexMask
= mutexWMask
172 mutexSema
= &mu
.wsema
175 old
:= atomic
.LoadUint64(&mu
.state
)
176 if old
&mutexBit
== 0 || old
&mutexRefMask
== 0 {
177 panic("net: inconsistent fdMutex")
179 // Drop lock, drop reference and wake read waiter if present.
180 new := (old
&^ mutexBit
) - mutexRef
181 if old
&mutexMask
!= 0 {
184 if atomic
.CompareAndSwapUint64(&mu
.state
, old
, new) {
185 if old
&mutexMask
!= 0 {
186 runtime_Semrelease(mutexSema
)
188 return new&(mutexClosed|mutexRefMask
) == mutexClosed
193 // Implemented in runtime package.
194 func runtime_Semacquire(sema
*uint32)
195 func runtime_Semrelease(sema
*uint32)
197 // incref adds a reference to fd.
198 // It returns an error when fd cannot be used.
199 func (fd
*netFD
) incref() error
{
200 if !fd
.fdmu
.incref() {
206 // decref removes a reference from fd.
207 // It also closes fd when the state of fd is set to closed and there
208 // is no remaining reference.
209 func (fd
*netFD
) decref() {
210 if fd
.fdmu
.decref() {
215 // readLock adds a reference to fd and locks fd for reading.
216 // It returns an error when fd cannot be used for reading.
217 func (fd
*netFD
) readLock() error
{
218 if !fd
.fdmu
.rwlock(true) {
224 // readUnlock removes a reference from fd and unlocks fd for reading.
225 // It also closes fd when the state of fd is set to closed and there
226 // is no remaining reference.
227 func (fd
*netFD
) readUnlock() {
228 if fd
.fdmu
.rwunlock(true) {
233 // writeLock adds a reference to fd and locks fd for writing.
234 // It returns an error when fd cannot be used for writing.
235 func (fd
*netFD
) writeLock() error
{
236 if !fd
.fdmu
.rwlock(false) {
242 // writeUnlock removes a reference from fd and unlocks fd for writing.
243 // It also closes fd when the state of fd is set to closed and there
244 // is no remaining reference.
245 func (fd
*netFD
) writeUnlock() {
246 if fd
.fdmu
.rwunlock(false) {