123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // +build darwin nacl netbsd openbsd plan9 solaris windows
- #include "runtime.h"
- // This implementation depends on OS-specific implementations of
- //
- // uintptr runtime_semacreate(void)
- // Create a semaphore, which will be assigned to m->waitsema.
- // The zero value is treated as absence of any semaphore,
- // so be sure to return a non-zero value.
- //
- // int32 runtime_semasleep(int64 ns)
- // If ns < 0, acquire m->waitsema and return 0.
- // If ns >= 0, try to acquire m->waitsema for at most ns nanoseconds.
- // Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
- //
- // int32 runtime_semawakeup(M *mp)
- // Wake up mp, which is or will soon be sleeping on mp->waitsema.
- //
- enum
- {
- LOCKED = 1,
- ACTIVE_SPIN = 4,
- ACTIVE_SPIN_CNT = 30,
- PASSIVE_SPIN = 1,
- };
- void
- runtime_lock(Lock *l)
- {
- M *m;
- uintptr v;
- uint32 i, spin;
- m = runtime_m();
- if(m->locks++ < 0)
- runtime_throw("runtime_lock: lock count");
- // Speculative grab for lock.
- if(runtime_casp((void**)&l->key, nil, (void*)LOCKED))
- return;
- if(m->waitsema == 0)
- m->waitsema = runtime_semacreate();
- // On uniprocessor's, no point spinning.
- // On multiprocessors, spin for ACTIVE_SPIN attempts.
- spin = 0;
- if(runtime_ncpu > 1)
- spin = ACTIVE_SPIN;
- for(i=0;; i++) {
- v = (uintptr)runtime_atomicloadp((void**)&l->key);
- if((v&LOCKED) == 0) {
- unlocked:
- if(runtime_casp((void**)&l->key, (void*)v, (void*)(v|LOCKED)))
- return;
- i = 0;
- }
- if(i<spin)
- runtime_procyield(ACTIVE_SPIN_CNT);
- else if(i<spin+PASSIVE_SPIN)
- runtime_osyield();
- else {
- // Someone else has it.
- // l->waitm points to a linked list of M's waiting
- // for this lock, chained through m->nextwaitm.
- // Queue this M.
- for(;;) {
- m->nextwaitm = (void*)(v&~LOCKED);
- if(runtime_casp((void**)&l->key, (void*)v, (void*)((uintptr)m|LOCKED)))
- break;
- v = (uintptr)runtime_atomicloadp((void**)&l->key);
- if((v&LOCKED) == 0)
- goto unlocked;
- }
- if(v&LOCKED) {
- // Queued. Wait.
- runtime_semasleep(-1);
- i = 0;
- }
- }
- }
- }
- void
- runtime_unlock(Lock *l)
- {
- uintptr v;
- M *mp;
- for(;;) {
- v = (uintptr)runtime_atomicloadp((void**)&l->key);
- if(v == LOCKED) {
- if(runtime_casp((void**)&l->key, (void*)LOCKED, nil))
- break;
- } else {
- // Other M's are waiting for the lock.
- // Dequeue an M.
- mp = (void*)(v&~LOCKED);
- if(runtime_casp((void**)&l->key, (void*)v, mp->nextwaitm)) {
- // Dequeued an M. Wake it.
- runtime_semawakeup(mp);
- break;
- }
- }
- }
- if(--runtime_m()->locks < 0)
- runtime_throw("runtime_unlock: lock count");
- }
- // One-time notifications.
- void
- runtime_noteclear(Note *n)
- {
- n->key = 0;
- }
- void
- runtime_notewakeup(Note *n)
- {
- M *mp;
- do
- mp = runtime_atomicloadp((void**)&n->key);
- while(!runtime_casp((void**)&n->key, mp, (void*)LOCKED));
- // Successfully set waitm to LOCKED.
- // What was it before?
- if(mp == nil) {
- // Nothing was waiting. Done.
- } else if(mp == (M*)LOCKED) {
- // Two notewakeups! Not allowed.
- runtime_throw("notewakeup - double wakeup");
- } else {
- // Must be the waiting m. Wake it up.
- runtime_semawakeup(mp);
- }
- }
- void
- runtime_notesleep(Note *n)
- {
- M *m;
- m = runtime_m();
- /* For gccgo it's OK to sleep in non-g0, and it happens in
- stoptheworld because we have not implemented preemption.
- if(runtime_g() != m->g0)
- runtime_throw("notesleep not on g0");
- */
- if(m->waitsema == 0)
- m->waitsema = runtime_semacreate();
- if(!runtime_casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup)
- if(n->key != LOCKED)
- runtime_throw("notesleep - waitm out of sync");
- return;
- }
- // Queued. Sleep.
- m->blocked = true;
- runtime_semasleep(-1);
- m->blocked = false;
- }
- static bool
- notetsleep(Note *n, int64 ns, int64 deadline, M *mp)
- {
- M *m;
- m = runtime_m();
- // Conceptually, deadline and mp are local variables.
- // They are passed as arguments so that the space for them
- // does not count against our nosplit stack sequence.
- // Register for wakeup on n->waitm.
- if(!runtime_casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup already)
- if(n->key != LOCKED)
- runtime_throw("notetsleep - waitm out of sync");
- return true;
- }
- if(ns < 0) {
- // Queued. Sleep.
- m->blocked = true;
- runtime_semasleep(-1);
- m->blocked = false;
- return true;
- }
- deadline = runtime_nanotime() + ns;
- for(;;) {
- // Registered. Sleep.
- m->blocked = true;
- if(runtime_semasleep(ns) >= 0) {
- m->blocked = false;
- // Acquired semaphore, semawakeup unregistered us.
- // Done.
- return true;
- }
- m->blocked = false;
- // Interrupted or timed out. Still registered. Semaphore not acquired.
- ns = deadline - runtime_nanotime();
- if(ns <= 0)
- break;
- // Deadline hasn't arrived. Keep sleeping.
- }
- // Deadline arrived. Still registered. Semaphore not acquired.
- // Want to give up and return, but have to unregister first,
- // so that any notewakeup racing with the return does not
- // try to grant us the semaphore when we don't expect it.
- for(;;) {
- mp = runtime_atomicloadp((void**)&n->key);
- if(mp == m) {
- // No wakeup yet; unregister if possible.
- if(runtime_casp((void**)&n->key, mp, nil))
- return false;
- } else if(mp == (M*)LOCKED) {
- // Wakeup happened so semaphore is available.
- // Grab it to avoid getting out of sync.
- m->blocked = true;
- if(runtime_semasleep(-1) < 0)
- runtime_throw("runtime: unable to acquire - semaphore out of sync");
- m->blocked = false;
- return true;
- } else
- runtime_throw("runtime: unexpected waitm - semaphore out of sync");
- }
- }
- bool
- runtime_notetsleep(Note *n, int64 ns)
- {
- M *m;
- bool res;
- m = runtime_m();
- if(runtime_g() != m->g0 && !m->gcing)
- runtime_throw("notetsleep not on g0");
- if(m->waitsema == 0)
- m->waitsema = runtime_semacreate();
- res = notetsleep(n, ns, 0, nil);
- return res;
- }
- // same as runtime_notetsleep, but called on user g (not g0)
- // calls only nosplit functions between entersyscallblock/exitsyscall
- bool
- runtime_notetsleepg(Note *n, int64 ns)
- {
- M *m;
- bool res;
- m = runtime_m();
- if(runtime_g() == m->g0)
- runtime_throw("notetsleepg on g0");
- if(m->waitsema == 0)
- m->waitsema = runtime_semacreate();
- runtime_entersyscallblock();
- res = notetsleep(n, ns, 0, nil);
- runtime_exitsyscall();
- return res;
- }
|