// Use of this source file is governed by a BSD-style // license that can be found in the LICENSE file.` #include "runtime.h" #include "defs.h" #include "os.h" #include "stack.h" enum { MUTEX_UNLOCKED = 0, MUTEX_LOCKED = 1, MUTEX_SLEEPING = 2, ACTIVE_SPIN = 4, ACTIVE_SPIN_CNT = 30, PASSIVE_SPIN = 1, ESRCH = 3, ENOTSUP = 91, }; extern SigTab runtime·sigtab[]; extern int64 runtime·rfork_thread(int32 flags, void *stack, M *m, G *g, void (*fn)(void)); extern int32 runtime·thrsleep(void *, void *, void*, void *); extern int32 runtime·thrwakeup(void *, int32); // From OpenBSD's #define CTL_HW 6 #define HW_NCPU 3 static int32 getncpu(void) { uint32 mib[2]; uint32 out; int32 ret; uintptr nout; // Fetch hw.ncpu via sysctl. mib[0] = CTL_HW; mib[1] = HW_NCPU; nout = sizeof out; out = 0; ret = runtime·sysctl(mib, 2, (byte*)&out, &nout, nil, 0); if(ret >= 0) return out; else return 1; } // Possible lock states are MUTEX_UNLOCKED, MUTEX_LOCKED and MUTEX_SLEEPING. // MUTEX_SLEEPING means that there is potentially at least one sleeping thread. // Note that there can be spinning threads during all states - they do not // affect the mutex's state. static void lock(Lock *l) { uint32 i, v, wait, spin; int32 ret; // Speculative grab for lock. v = runtime·xchg(&l->key, MUTEX_LOCKED); if(v == MUTEX_UNLOCKED) return; // If we ever change the lock from MUTEX_SLEEPING to some other value, // we must be careful to change it back to MUTEX_SLEEPING before // returning, to ensure that the sleeping thread gets its wakeup call. wait = v; // No point spinning unless there are multiple processors. spin = 0; if(runtime·ncpu > 1) spin = ACTIVE_SPIN; for(;;) { // Try for lock, spinning. for(i = 0; i < spin; i++) { while(l->key == MUTEX_UNLOCKED) if(runtime·cas(&l->key, MUTEX_UNLOCKED, wait)) return; runtime·procyield(ACTIVE_SPIN_CNT); } // Try for lock, rescheduling. for(i = 0; i < PASSIVE_SPIN; i++) { while(l->key == MUTEX_UNLOCKED) if(runtime·cas(&l->key, MUTEX_UNLOCKED, wait)) return; runtime·osyield(); } // Grab a lock on sema and sleep - sema will be unlocked by // thrsleep() and we'll get woken by another thread. // Note that thrsleep unlocks on a _spinlock_lock_t which is // an int on amd64, so we need to be careful here. while (!runtime·cas(&l->sema, MUTEX_UNLOCKED, MUTEX_LOCKED)) runtime·osyield(); v = runtime·xchg(&l->key, MUTEX_SLEEPING); if(v == MUTEX_UNLOCKED) { l->sema = MUTEX_UNLOCKED; return; } wait = v; ret = runtime·thrsleep(&l->key, 0, 0, &l->sema); if (ret != 0) { runtime·printf("thrsleep addr=%p sema=%d ret=%d\n", &l->key, l->sema, ret); l->sema = MUTEX_UNLOCKED; } } } static void unlock(Lock *l) { uint32 v, ret; while (!runtime·cas(&l->sema, MUTEX_UNLOCKED, MUTEX_LOCKED)) runtime·osyield(); v = runtime·xchg(&l->key, MUTEX_UNLOCKED); l->sema = MUTEX_UNLOCKED; if(v == MUTEX_UNLOCKED) runtime·throw("unlock of unlocked lock"); if(v == MUTEX_SLEEPING) { ret = runtime·thrwakeup(&l->key, 0); if (ret != 0 && ret != ESRCH) { runtime·printf("thrwakeup addr=%p sem=%d ret=%d\n", &l->key, l->sema, ret); } } } void runtime·lock(Lock *l) { if(m->locks < 0) runtime·throw("lock count"); m->locks++; lock(l); } void runtime·unlock(Lock *l) { m->locks--; if(m->locks < 0) runtime·throw("lock count"); unlock(l); } // Event notifications. void runtime·noteclear(Note *n) { n->lock.key = 0; lock(&n->lock); } void runtime·notesleep(Note *n) { lock(&n->lock); unlock(&n->lock); } void runtime·notewakeup(Note *n) { unlock(&n->lock); } // From OpenBSD's sys/param.h #define RFPROC (1<<4) /* change child (else changes curproc) */ #define RFMEM (1<<5) /* share `address space' */ #define RFNOWAIT (1<<6) /* parent need not wait() on child */ #define RFTHREAD (1<<13) /* create a thread, not a process */ void runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) { int32 flags; int32 ret; flags = RFPROC | RFTHREAD | RFMEM | RFNOWAIT; if (0) { runtime·printf( "newosproc stk=%p m=%p g=%p fn=%p id=%d/%d ostk=%p\n", stk, m, g, fn, m->id, m->tls[0], &m); } m->tls[0] = m->id; // so 386 asm can find it if((ret = runtime·rfork_thread(flags, stk, m, g, fn)) < 0) { runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount() - 1, -ret); if (ret == -ENOTSUP) runtime·printf("runtime: is kern.rthreads disabled?\n"); runtime·throw("runtime.newosproc"); } } void runtime·osinit(void) { runtime·ncpu = getncpu(); } void runtime·goenvs(void) { runtime·goenvs_unix(); } // Called to initialize a new m (including the bootstrap m). void runtime·minit(void) { // Initialize signal handling m->gsignal = runtime·malg(32*1024); runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024); } void runtime·sigpanic(void) { switch(g->sig) { case SIGBUS: if(g->sigcode0 == BUS_ADRERR && g->sigcode1 < 0x1000) runtime·panicstring("invalid memory address or nil pointer dereference"); runtime·printf("unexpected fault address %p\n", g->sigcode1); runtime·throw("fault"); case SIGSEGV: if((g->sigcode0 == 0 || g->sigcode0 == SEGV_MAPERR || g->sigcode0 == SEGV_ACCERR) && g->sigcode1 < 0x1000) runtime·panicstring("invalid memory address or nil pointer dereference"); runtime·printf("unexpected fault address %p\n", g->sigcode1); runtime·throw("fault"); case SIGFPE: switch(g->sigcode0) { case FPE_INTDIV: runtime·panicstring("integer divide by zero"); case FPE_INTOVF: runtime·panicstring("integer overflow"); } runtime·panicstring("floating point error"); } runtime·panicstring(runtime·sigtab[g->sig].name); }