1 // 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.
5 // +build darwin nacl netbsd openbsd plan9 solaris windows
9 // This implementation depends on OS-specific implementations of
11 // uintptr runtime_semacreate(void)
12 // Create a semaphore, which will be assigned to m->waitsema.
13 // The zero value is treated as absence of any semaphore,
14 // so be sure to return a non-zero value.
16 // int32 runtime_semasleep(int64 ns)
17 // If ns < 0, acquire m->waitsema and return 0.
18 // If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
19 // Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
21 // int32 runtime_semawakeup(M *mp)
22 // Wake up mp, which is or will soon be sleeping on mp->waitsema.
43 runtime_throw("runtime_lock: lock count");
45 // Speculative grab for lock.
46 if(runtime_casp((void**)&l
->key
, nil
, (void*)LOCKED
))
50 m
->waitsema
= runtime_semacreate();
52 // On uniprocessor's, no point spinning.
53 // On multiprocessors, spin for ACTIVE_SPIN attempts.
59 v
= (uintptr
)runtime_atomicloadp((void**)&l
->key
);
62 if(runtime_casp((void**)&l
->key
, (void*)v
, (void*)(v
|LOCKED
)))
67 runtime_procyield(ACTIVE_SPIN_CNT
);
68 else if(i
<spin
+PASSIVE_SPIN
)
71 // Someone else has it.
72 // l->waitm points to a linked list of M's waiting
73 // for this lock, chained through m->nextwaitm.
76 m
->nextwaitm
= (void*)(v
&~LOCKED
);
77 if(runtime_casp((void**)&l
->key
, (void*)v
, (void*)((uintptr
)m
|LOCKED
)))
79 v
= (uintptr
)runtime_atomicloadp((void**)&l
->key
);
85 runtime_semasleep(-1);
93 runtime_unlock(Lock
*l
)
99 v
= (uintptr
)runtime_atomicloadp((void**)&l
->key
);
101 if(runtime_casp((void**)&l
->key
, (void*)LOCKED
, nil
))
104 // Other M's are waiting for the lock.
106 mp
= (void*)(v
&~LOCKED
);
107 if(runtime_casp((void**)&l
->key
, (void*)v
, mp
->nextwaitm
)) {
108 // Dequeued an M. Wake it.
109 runtime_semawakeup(mp
);
115 if(--runtime_m()->locks
< 0)
116 runtime_throw("runtime_unlock: lock count");
119 // One-time notifications.
121 runtime_noteclear(Note
*n
)
127 runtime_notewakeup(Note
*n
)
132 mp
= runtime_atomicloadp((void**)&n
->key
);
133 while(!runtime_casp((void**)&n
->key
, mp
, (void*)LOCKED
));
135 // Successfully set waitm to LOCKED.
136 // What was it before?
138 // Nothing was waiting. Done.
139 } else if(mp
== (M
*)LOCKED
) {
140 // Two notewakeups! Not allowed.
141 runtime_throw("notewakeup - double wakeup");
143 // Must be the waiting m. Wake it up.
144 runtime_semawakeup(mp
);
149 runtime_notesleep(Note
*n
)
155 /* For gccgo it's OK to sleep in non-g0, and it happens in
156 stoptheworld because we have not implemented preemption.
158 if(runtime_g() != m->g0)
159 runtime_throw("notesleep not on g0");
163 m
->waitsema
= runtime_semacreate();
164 if(!runtime_casp((void**)&n
->key
, nil
, m
)) { // must be LOCKED (got wakeup)
166 runtime_throw("notesleep - waitm out of sync");
171 runtime_semasleep(-1);
176 notetsleep(Note
*n
, int64 ns
, int64 deadline
, M
*mp
)
182 // Conceptually, deadline and mp are local variables.
183 // They are passed as arguments so that the space for them
184 // does not count against our nosplit stack sequence.
186 // Register for wakeup on n->waitm.
187 if(!runtime_casp((void**)&n
->key
, nil
, m
)) { // must be LOCKED (got wakeup already)
189 runtime_throw("notetsleep - waitm out of sync");
196 runtime_semasleep(-1);
201 deadline
= runtime_nanotime() + ns
;
203 // Registered. Sleep.
205 if(runtime_semasleep(ns
) >= 0) {
207 // Acquired semaphore, semawakeup unregistered us.
213 // Interrupted or timed out. Still registered. Semaphore not acquired.
214 ns
= deadline
- runtime_nanotime();
217 // Deadline hasn't arrived. Keep sleeping.
220 // Deadline arrived. Still registered. Semaphore not acquired.
221 // Want to give up and return, but have to unregister first,
222 // so that any notewakeup racing with the return does not
223 // try to grant us the semaphore when we don't expect it.
225 mp
= runtime_atomicloadp((void**)&n
->key
);
227 // No wakeup yet; unregister if possible.
228 if(runtime_casp((void**)&n
->key
, mp
, nil
))
230 } else if(mp
== (M
*)LOCKED
) {
231 // Wakeup happened so semaphore is available.
232 // Grab it to avoid getting out of sync.
234 if(runtime_semasleep(-1) < 0)
235 runtime_throw("runtime: unable to acquire - semaphore out of sync");
239 runtime_throw("runtime: unexpected waitm - semaphore out of sync");
244 runtime_notetsleep(Note
*n
, int64 ns
)
251 if(runtime_g() != m
->g0
&& !m
->gcing
)
252 runtime_throw("notetsleep not on g0");
255 m
->waitsema
= runtime_semacreate();
257 res
= notetsleep(n
, ns
, 0, nil
);
261 // same as runtime_notetsleep, but called on user g (not g0)
262 // calls only nosplit functions between entersyscallblock/exitsyscall
264 runtime_notetsleepg(Note
*n
, int64 ns
)
271 if(runtime_g() == m
->g0
)
272 runtime_throw("notetsleepg on g0");
275 m
->waitsema
= runtime_semacreate();
277 runtime_entersyscallblock();
278 res
= notetsleep(n
, ns
, 0, nil
);
279 runtime_exitsyscall();