Skip to content

Commit 655c8d7

Browse files
committed
add futex
1 parent 00323d0 commit 655c8d7

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

futex.md

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
# Futex
2+
3+
```go
4+
// This implementation depends on OS-specific implementations of
5+
//
6+
// futexsleep(addr *uint32, val uint32, ns int64)
7+
// Atomically,
8+
// if *addr == val { sleep }
9+
// Might be woken up spuriously; that's allowed.
10+
// Don't sleep longer than ns; ns < 0 means forever.
11+
//
12+
// futexwakeup(addr *uint32, cnt uint32)
13+
// If any procs are sleeping on addr, wake up at most cnt.
14+
15+
const (
16+
mutex_unlocked = 0
17+
mutex_locked = 1
18+
mutex_sleeping = 2
19+
20+
active_spin = 4
21+
active_spin_cnt = 30
22+
passive_spin = 1
23+
)
24+
25+
// Possible lock states are mutex_unlocked, mutex_locked and mutex_sleeping.
26+
// mutex_sleeping means that there is presumably at least one sleeping thread.
27+
// Note that there can be spinning threads during all states - they do not
28+
// affect mutex's state.
29+
30+
// We use the uintptr mutex.key and note.key as a uint32.
31+
//go:nosplit
32+
func key32(p *uintptr) *uint32 {
33+
return (*uint32)(unsafe.Pointer(p))
34+
}
35+
36+
func lock(l *mutex) {
37+
gp := getg()
38+
39+
if gp.m.locks < 0 {
40+
throw("runtime·lock: lock count")
41+
}
42+
gp.m.locks++
43+
44+
// Speculative grab for lock.
45+
v := atomic.Xchg(key32(&l.key), mutex_locked)
46+
if v == mutex_unlocked {
47+
return
48+
}
49+
50+
// wait is either MUTEX_LOCKED or MUTEX_SLEEPING
51+
// depending on whether there is a thread sleeping
52+
// on this mutex. If we ever change l->key from
53+
// MUTEX_SLEEPING to some other value, we must be
54+
// careful to change it back to MUTEX_SLEEPING before
55+
// returning, to ensure that the sleeping thread gets
56+
// its wakeup call.
57+
wait := v
58+
59+
// On uniprocessors, no point spinning.
60+
// On multiprocessors, spin for ACTIVE_SPIN attempts.
61+
spin := 0
62+
if ncpu > 1 {
63+
spin = active_spin
64+
}
65+
for {
66+
// Try for lock, spinning.
67+
for i := 0; i < spin; i++ {
68+
for l.key == mutex_unlocked {
69+
if atomic.Cas(key32(&l.key), mutex_unlocked, wait) {
70+
return
71+
}
72+
}
73+
procyield(active_spin_cnt)
74+
}
75+
76+
// Try for lock, rescheduling.
77+
for i := 0; i < passive_spin; i++ {
78+
for l.key == mutex_unlocked {
79+
if atomic.Cas(key32(&l.key), mutex_unlocked, wait) {
80+
return
81+
}
82+
}
83+
osyield()
84+
}
85+
86+
// Sleep.
87+
v = atomic.Xchg(key32(&l.key), mutex_sleeping)
88+
if v == mutex_unlocked {
89+
return
90+
}
91+
wait = mutex_sleeping
92+
futexsleep(key32(&l.key), mutex_sleeping, -1)
93+
}
94+
}
95+
96+
func unlock(l *mutex) {
97+
v := atomic.Xchg(key32(&l.key), mutex_unlocked)
98+
if v == mutex_unlocked {
99+
throw("unlock of unlocked lock")
100+
}
101+
if v == mutex_sleeping {
102+
futexwakeup(key32(&l.key), 1)
103+
}
104+
105+
gp := getg()
106+
gp.m.locks--
107+
if gp.m.locks < 0 {
108+
throw("runtime·unlock: lock count")
109+
}
110+
if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack
111+
gp.stackguard0 = stackPreempt
112+
}
113+
}
114+
115+
// One-time notifications.
116+
func noteclear(n *note) {
117+
n.key = 0
118+
}
119+
120+
func notewakeup(n *note) {
121+
old := atomic.Xchg(key32(&n.key), 1)
122+
if old != 0 {
123+
print("notewakeup - double wakeup (", old, ")\n")
124+
throw("notewakeup - double wakeup")
125+
}
126+
futexwakeup(key32(&n.key), 1)
127+
}
128+
129+
func notesleep(n *note) {
130+
gp := getg()
131+
if gp != gp.m.g0 {
132+
throw("notesleep not on g0")
133+
}
134+
ns := int64(-1)
135+
if *cgo_yield != nil {
136+
// Sleep for an arbitrary-but-moderate interval to poll libc interceptors.
137+
ns = 10e6
138+
}
139+
for atomic.Load(key32(&n.key)) == 0 {
140+
gp.m.blocked = true
141+
futexsleep(key32(&n.key), 0, ns)
142+
if *cgo_yield != nil {
143+
asmcgocall(*cgo_yield, nil)
144+
}
145+
gp.m.blocked = false
146+
}
147+
}
148+
149+
// May run with m.p==nil if called from notetsleep, so write barriers
150+
// are not allowed.
151+
//
152+
//go:nosplit
153+
//go:nowritebarrier
154+
func notetsleep_internal(n *note, ns int64) bool {
155+
gp := getg()
156+
157+
if ns < 0 {
158+
if *cgo_yield != nil {
159+
// Sleep for an arbitrary-but-moderate interval to poll libc interceptors.
160+
ns = 10e6
161+
}
162+
for atomic.Load(key32(&n.key)) == 0 {
163+
gp.m.blocked = true
164+
futexsleep(key32(&n.key), 0, ns)
165+
if *cgo_yield != nil {
166+
asmcgocall(*cgo_yield, nil)
167+
}
168+
gp.m.blocked = false
169+
}
170+
return true
171+
}
172+
173+
if atomic.Load(key32(&n.key)) != 0 {
174+
return true
175+
}
176+
177+
deadline := nanotime() + ns
178+
for {
179+
if *cgo_yield != nil && ns > 10e6 {
180+
ns = 10e6
181+
}
182+
gp.m.blocked = true
183+
futexsleep(key32(&n.key), 0, ns)
184+
if *cgo_yield != nil {
185+
asmcgocall(*cgo_yield, nil)
186+
}
187+
gp.m.blocked = false
188+
if atomic.Load(key32(&n.key)) != 0 {
189+
break
190+
}
191+
now := nanotime()
192+
if now >= deadline {
193+
break
194+
}
195+
ns = deadline - now
196+
}
197+
return atomic.Load(key32(&n.key)) != 0
198+
}
199+
200+
func notetsleep(n *note, ns int64) bool {
201+
gp := getg()
202+
if gp != gp.m.g0 && gp.m.preemptoff != "" {
203+
throw("notetsleep not on g0")
204+
}
205+
206+
return notetsleep_internal(n, ns)
207+
}
208+
209+
// same as runtime·notetsleep, but called on user g (not g0)
210+
// calls only nosplit functions between entersyscallblock/exitsyscall
211+
func notetsleepg(n *note, ns int64) bool {
212+
gp := getg()
213+
if gp == gp.m.g0 {
214+
throw("notetsleepg on g0")
215+
}
216+
217+
entersyscallblock(0)
218+
ok := notetsleep_internal(n, ns)
219+
exitsyscall(0)
220+
return ok
221+
}
222+
223+
```
224+
225+
参考资料:
226+
227+
https://zh.wikipedia.org/zh-hans/Futex
228+
229+
http://man7.org/linux/man-pages/man2/futex.2.html
230+
231+
https://blog.csdn.net/jianchaolv/article/details/7544316
232+
233+
http://blog.sina.com.cn/s/blog_e59371cc0102v29b.html
234+
235+
https://www.jianshu.com/p/570a61f08e27
236+
237+
https://eli.thegreenplace.net/2018/basics-of-futexes/

0 commit comments

Comments
 (0)