blob: 6d5509d7f2a3c7f528adb66a33dc2c2ec20c8e54 [file] [log] [blame]
Dmitriy Vyukov23e15f72013-08-09 21:43:00 +04001// 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
5package net
6
7import "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.
12type 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.
25const (
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
48func (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
64func (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
93func (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 Vyukov727dd082013-08-16 16:02:55 +0400101 return new&(mutexClosed|mutexRefMask) == mutexClosed
Dmitriy Vyukov23e15f72013-08-09 21:43:00 +0400102 }
103 }
104}
105
106func (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
149func (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 Vyukov727dd082013-08-16 16:02:55 +0400177 return new&(mutexClosed|mutexRefMask) == mutexClosed
Dmitriy Vyukov23e15f72013-08-09 21:43:00 +0400178 }
179 }
180}
181
182// Implemented in runtime package.
183func runtime_Semacquire(sema *uint32)
184func runtime_Semrelease(sema *uint32)