| // 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 <sys/sysctl.h> | |
| #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); | |
| } |