blob: d9d91c915534f25f7ab0498a7b9d0da669404990 [file] [log] [blame]
Russ Cox3a7f6642014-08-29 16:20:48 -04001// Copyright 2011 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// +build darwin nacl netbsd openbsd plan9 solaris windows
6
7package runtime
8
9import "unsafe"
10
11// This implementation depends on OS-specific implementations of
12//
13// uintptr runtime·semacreate(void)
14// Create a semaphore, which will be assigned to m->waitsema.
15// The zero value is treated as absence of any semaphore,
16// so be sure to return a non-zero value.
17//
18// int32 runtime·semasleep(int64 ns)
19// If ns < 0, acquire m->waitsema and return 0.
20// If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
21// Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
22//
23// int32 runtime·semawakeup(M *mp)
24// Wake up mp, which is or will soon be sleeping on mp->waitsema.
25//
26const (
27 locked uintptr = 1
28
29 active_spin = 4
30 active_spin_cnt = 30
31 passive_spin = 1
32)
33
Russ Cox3a7f6642014-08-29 16:20:48 -040034func lock(l *mutex) {
35 gp := getg()
36 if gp.m.locks < 0 {
Keith Randallb2a950b2014-12-27 20:58:00 -080037 throw("runtime·lock: lock count")
Russ Cox3a7f6642014-08-29 16:20:48 -040038 }
39 gp.m.locks++
40
41 // Speculative grab for lock.
42 if casuintptr(&l.key, 0, locked) {
43 return
44 }
45 if gp.m.waitsema == 0 {
46 gp.m.waitsema = semacreate()
47 }
48
49 // On uniprocessor's, no point spinning.
50 // On multiprocessors, spin for ACTIVE_SPIN attempts.
51 spin := 0
52 if ncpu > 1 {
53 spin = active_spin
54 }
55Loop:
56 for i := 0; ; i++ {
57 v := atomicloaduintptr(&l.key)
58 if v&locked == 0 {
59 // Unlocked. Try to lock.
60 if casuintptr(&l.key, v, v|locked) {
61 return
62 }
63 i = 0
64 }
65 if i < spin {
66 procyield(active_spin_cnt)
67 } else if i < spin+passive_spin {
68 osyield()
69 } else {
70 // Someone else has it.
71 // l->waitm points to a linked list of M's waiting
72 // for this lock, chained through m->nextwaitm.
73 // Queue this M.
74 for {
Russ Cox87ec06f2015-03-17 15:07:05 -040075 gp.m.nextwaitm = v &^ locked
Russ Cox3a7f6642014-08-29 16:20:48 -040076 if casuintptr(&l.key, v, uintptr(unsafe.Pointer(gp.m))|locked) {
77 break
78 }
79 v = atomicloaduintptr(&l.key)
80 if v&locked == 0 {
81 continue Loop
82 }
83 }
84 if v&locked != 0 {
85 // Queued. Wait.
86 semasleep(-1)
87 i = 0
88 }
89 }
90 }
91}
92
Russ Cox87ec06f2015-03-17 15:07:05 -040093//go:nowritebarrier
94// We might not be holding a p in this code.
Russ Cox3a7f6642014-08-29 16:20:48 -040095func unlock(l *mutex) {
96 gp := getg()
97 var mp *m
98 for {
99 v := atomicloaduintptr(&l.key)
100 if v == locked {
101 if casuintptr(&l.key, locked, 0) {
102 break
103 }
104 } else {
105 // Other M's are waiting for the lock.
106 // Dequeue an M.
107 mp = (*m)((unsafe.Pointer)(v &^ locked))
Russ Cox87ec06f2015-03-17 15:07:05 -0400108 if casuintptr(&l.key, v, mp.nextwaitm) {
Russ Cox3a7f6642014-08-29 16:20:48 -0400109 // Dequeued an M. Wake it.
110 semawakeup(mp)
111 break
112 }
113 }
114 }
115 gp.m.locks--
116 if gp.m.locks < 0 {
Keith Randallb2a950b2014-12-27 20:58:00 -0800117 throw("runtime·unlock: lock count")
Russ Cox3a7f6642014-08-29 16:20:48 -0400118 }
119 if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack
Russ Coxe6d35112015-01-05 16:29:21 +0000120 gp.stackguard0 = stackPreempt
Russ Cox3a7f6642014-08-29 16:20:48 -0400121 }
122}
123
124// One-time notifications.
125func noteclear(n *note) {
126 n.key = 0
127}
128
129func notewakeup(n *note) {
130 var v uintptr
131 for {
132 v = atomicloaduintptr(&n.key)
133 if casuintptr(&n.key, v, locked) {
134 break
135 }
136 }
137
138 // Successfully set waitm to locked.
139 // What was it before?
140 switch {
141 case v == 0:
142 // Nothing was waiting. Done.
143 case v == locked:
144 // Two notewakeups! Not allowed.
Keith Randallb2a950b2014-12-27 20:58:00 -0800145 throw("notewakeup - double wakeup")
Russ Cox3a7f6642014-08-29 16:20:48 -0400146 default:
147 // Must be the waiting m. Wake it up.
148 semawakeup((*m)(unsafe.Pointer(v)))
149 }
150}
151
152func notesleep(n *note) {
153 gp := getg()
154 if gp != gp.m.g0 {
Keith Randallb2a950b2014-12-27 20:58:00 -0800155 throw("notesleep not on g0")
Russ Cox3a7f6642014-08-29 16:20:48 -0400156 }
157 if gp.m.waitsema == 0 {
158 gp.m.waitsema = semacreate()
159 }
160 if !casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
161 // Must be locked (got wakeup).
162 if n.key != locked {
Keith Randallb2a950b2014-12-27 20:58:00 -0800163 throw("notesleep - waitm out of sync")
Russ Cox3a7f6642014-08-29 16:20:48 -0400164 }
165 return
166 }
167 // Queued. Sleep.
168 gp.m.blocked = true
169 semasleep(-1)
170 gp.m.blocked = false
171}
172
173//go:nosplit
Russ Cox93805d72014-09-03 23:10:15 -0400174func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool {
175 // gp and deadline are logically local variables, but they are written
176 // as parameters so that the stack space they require is charged
177 // to the caller.
178 // This reduces the nosplit footprint of notetsleep_internal.
179 gp = getg()
180
Russ Cox3a7f6642014-08-29 16:20:48 -0400181 // Register for wakeup on n->waitm.
182 if !casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
183 // Must be locked (got wakeup).
184 if n.key != locked {
Keith Randallb2a950b2014-12-27 20:58:00 -0800185 throw("notetsleep - waitm out of sync")
Russ Cox3a7f6642014-08-29 16:20:48 -0400186 }
187 return true
188 }
189 if ns < 0 {
190 // Queued. Sleep.
191 gp.m.blocked = true
192 semasleep(-1)
193 gp.m.blocked = false
194 return true
195 }
Russ Cox93805d72014-09-03 23:10:15 -0400196
197 deadline = nanotime() + ns
Russ Cox3a7f6642014-08-29 16:20:48 -0400198 for {
199 // Registered. Sleep.
200 gp.m.blocked = true
201 if semasleep(ns) >= 0 {
202 gp.m.blocked = false
203 // Acquired semaphore, semawakeup unregistered us.
204 // Done.
205 return true
206 }
207 gp.m.blocked = false
208 // Interrupted or timed out. Still registered. Semaphore not acquired.
209 ns = deadline - nanotime()
210 if ns <= 0 {
211 break
212 }
213 // Deadline hasn't arrived. Keep sleeping.
214 }
215
216 // Deadline arrived. Still registered. Semaphore not acquired.
217 // Want to give up and return, but have to unregister first,
218 // so that any notewakeup racing with the return does not
219 // try to grant us the semaphore when we don't expect it.
220 for {
221 v := atomicloaduintptr(&n.key)
222 switch v {
223 case uintptr(unsafe.Pointer(gp.m)):
224 // No wakeup yet; unregister if possible.
225 if casuintptr(&n.key, v, 0) {
226 return false
227 }
228 case locked:
229 // Wakeup happened so semaphore is available.
230 // Grab it to avoid getting out of sync.
231 gp.m.blocked = true
232 if semasleep(-1) < 0 {
Keith Randallb2a950b2014-12-27 20:58:00 -0800233 throw("runtime: unable to acquire - semaphore out of sync")
Russ Cox3a7f6642014-08-29 16:20:48 -0400234 }
235 gp.m.blocked = false
236 return true
237 default:
Keith Randallb2a950b2014-12-27 20:58:00 -0800238 throw("runtime: unexpected waitm - semaphore out of sync")
Russ Cox3a7f6642014-08-29 16:20:48 -0400239 }
240 }
241}
242
243func notetsleep(n *note, ns int64) bool {
244 gp := getg()
Austin Clements28b51182015-01-30 15:30:41 -0500245 if gp != gp.m.g0 && gp.m.preemptoff != "" {
Keith Randallb2a950b2014-12-27 20:58:00 -0800246 throw("notetsleep not on g0")
Russ Cox3a7f6642014-08-29 16:20:48 -0400247 }
248 if gp.m.waitsema == 0 {
249 gp.m.waitsema = semacreate()
250 }
Russ Cox93805d72014-09-03 23:10:15 -0400251 return notetsleep_internal(n, ns, nil, 0)
Russ Cox3a7f6642014-08-29 16:20:48 -0400252}
253
254// same as runtime·notetsleep, but called on user g (not g0)
255// calls only nosplit functions between entersyscallblock/exitsyscall
256func notetsleepg(n *note, ns int64) bool {
257 gp := getg()
258 if gp == gp.m.g0 {
Keith Randallb2a950b2014-12-27 20:58:00 -0800259 throw("notetsleepg on g0")
Russ Cox3a7f6642014-08-29 16:20:48 -0400260 }
261 if gp.m.waitsema == 0 {
262 gp.m.waitsema = semacreate()
263 }
Russ Coxb2cdf302014-11-11 17:08:33 -0500264 entersyscallblock(0)
Russ Cox93805d72014-09-03 23:10:15 -0400265 ok := notetsleep_internal(n, ns, nil, 0)
Russ Coxb2cdf302014-11-11 17:08:33 -0500266 exitsyscall(0)
Russ Cox3a7f6642014-08-29 16:20:48 -0400267 return ok
268}