summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/darwin/thread.c
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
committerOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
commit5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch)
treec0650497e988f47be9c6f2324fa692a52dea82e1 /src/pkg/runtime/darwin/thread.c
parent80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff)
downloadgolang-upstream/60.tar.gz
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/pkg/runtime/darwin/thread.c')
-rw-r--r--src/pkg/runtime/darwin/thread.c484
1 files changed, 484 insertions, 0 deletions
diff --git a/src/pkg/runtime/darwin/thread.c b/src/pkg/runtime/darwin/thread.c
new file mode 100644
index 000000000..6733e815e
--- /dev/null
+++ b/src/pkg/runtime/darwin/thread.c
@@ -0,0 +1,484 @@
+// Copyright 2009 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.
+
+#include "runtime.h"
+#include "defs.h"
+#include "os.h"
+#include "stack.h"
+
+extern SigTab runtime·sigtab[];
+
+static void
+unimplemented(int8 *name)
+{
+ runtime·prints(name);
+ runtime·prints(" not implemented\n");
+ *(int32*)1231 = 1231;
+}
+
+// Thread-safe allocation of a semaphore.
+// Psema points at a kernel semaphore key.
+// It starts out zero, meaning no semaphore.
+// Fill it in, being careful of others calling initsema
+// simultaneously.
+static void
+initsema(uint32 *psema)
+{
+ uint32 sema;
+
+ if(*psema != 0) // already have one
+ return;
+
+ sema = runtime·mach_semcreate();
+ if(!runtime·cas(psema, 0, sema)){
+ // Someone else filled it in. Use theirs.
+ runtime·mach_semdestroy(sema);
+ return;
+ }
+}
+
+
+// Blocking locks.
+
+// Implement Locks, using semaphores.
+// l->key is the number of threads who want the lock.
+// In a race, one thread increments l->key from 0 to 1
+// and the others increment it from >0 to >1. The thread
+// who does the 0->1 increment gets the lock, and the
+// others wait on the semaphore. When the 0->1 thread
+// releases the lock by decrementing l->key, l->key will
+// be >0, so it will increment the semaphore to wake up
+// one of the others. This is the same algorithm used
+// in Plan 9's user-level locks.
+
+void
+runtime·lock(Lock *l)
+{
+ if(m->locks < 0)
+ runtime·throw("lock count");
+ m->locks++;
+
+ if(runtime·xadd(&l->key, 1) > 1) { // someone else has it; wait
+ // Allocate semaphore if needed.
+ if(l->sema == 0)
+ initsema(&l->sema);
+ runtime·mach_semacquire(l->sema);
+ }
+}
+
+void
+runtime·unlock(Lock *l)
+{
+ m->locks--;
+ if(m->locks < 0)
+ runtime·throw("lock count");
+
+ if(runtime·xadd(&l->key, -1) > 0) { // someone else is waiting
+ // Allocate semaphore if needed.
+ if(l->sema == 0)
+ initsema(&l->sema);
+ runtime·mach_semrelease(l->sema);
+ }
+}
+
+static void
+destroylock(Lock *l)
+{
+ if(l->sema != 0) {
+ runtime·mach_semdestroy(l->sema);
+ l->sema = 0;
+ }
+}
+
+// User-level semaphore implementation:
+// try to do the operations in user space on u,
+// but when it's time to block, fall back on the kernel semaphore k.
+// This is the same algorithm used in Plan 9.
+void
+runtime·usemacquire(Usema *s)
+{
+ if((int32)runtime·xadd(&s->u, -1) < 0) {
+ if(s->k == 0)
+ initsema(&s->k);
+ runtime·mach_semacquire(s->k);
+ }
+}
+
+void
+runtime·usemrelease(Usema *s)
+{
+ if((int32)runtime·xadd(&s->u, 1) <= 0) {
+ if(s->k == 0)
+ initsema(&s->k);
+ runtime·mach_semrelease(s->k);
+ }
+}
+
+
+// Event notifications.
+void
+runtime·noteclear(Note *n)
+{
+ n->wakeup = 0;
+}
+
+void
+runtime·notesleep(Note *n)
+{
+ while(!n->wakeup)
+ runtime·usemacquire(&n->sema);
+}
+
+void
+runtime·notewakeup(Note *n)
+{
+ n->wakeup = 1;
+ runtime·usemrelease(&n->sema);
+}
+
+
+// BSD interface for threading.
+void
+runtime·osinit(void)
+{
+ // Register our thread-creation callback (see {amd64,386}/sys.s)
+ // but only if we're not using cgo. If we are using cgo we need
+ // to let the C pthread libary install its own thread-creation callback.
+ if(!runtime·iscgo)
+ runtime·bsdthread_register();
+ runtime·destroylock = destroylock;
+}
+
+void
+runtime·goenvs(void)
+{
+ runtime·goenvs_unix();
+}
+
+void
+runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void))
+{
+ int32 errno;
+
+ m->tls[0] = m->id; // so 386 asm can find it
+ 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);
+ }
+ if((errno = runtime·bsdthread_create(stk, m, g, fn)) < 0) {
+ runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), -errno);
+ runtime·throw("runtime.newosproc");
+ }
+}
+
+// Called to initialize a new m (including the bootstrap m).
+void
+runtime·minit(void)
+{
+ // Initialize signal handling.
+ m->gsignal = runtime·malg(32*1024); // OS X wants >=8K, Linux >=2K
+ runtime·signalstack(m->gsignal->stackguard - StackGuard, 32*1024);
+}
+
+// Mach IPC, to get at semaphores
+// Definitions are in /usr/include/mach on a Mac.
+
+static void
+macherror(int32 r, int8 *fn)
+{
+ runtime·printf("mach error %s: %d\n", fn, r);
+ runtime·throw("mach error");
+}
+
+enum
+{
+ DebugMach = 0
+};
+
+static MachNDR zerondr;
+
+#define MACH_MSGH_BITS(a, b) ((a) | ((b)<<8))
+
+static int32
+mach_msg(MachHeader *h,
+ int32 op,
+ uint32 send_size,
+ uint32 rcv_size,
+ uint32 rcv_name,
+ uint32 timeout,
+ uint32 notify)
+{
+ // TODO: Loop on interrupt.
+ return runtime·mach_msg_trap(h, op, send_size, rcv_size, rcv_name, timeout, notify);
+}
+
+// Mach RPC (MIG)
+
+enum
+{
+ MinMachMsg = 48,
+ Reply = 100,
+};
+
+#pragma pack on
+typedef struct CodeMsg CodeMsg;
+struct CodeMsg
+{
+ MachHeader h;
+ MachNDR NDR;
+ int32 code;
+};
+#pragma pack off
+
+static int32
+machcall(MachHeader *h, int32 maxsize, int32 rxsize)
+{
+ uint32 *p;
+ int32 i, ret, id;
+ uint32 port;
+ CodeMsg *c;
+
+ if((port = m->machport) == 0){
+ port = runtime·mach_reply_port();
+ m->machport = port;
+ }
+
+ h->msgh_bits |= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ h->msgh_local_port = port;
+ h->msgh_reserved = 0;
+ id = h->msgh_id;
+
+ if(DebugMach){
+ p = (uint32*)h;
+ runtime·prints("send:\t");
+ for(i=0; i<h->msgh_size/sizeof(p[0]); i++){
+ runtime·prints(" ");
+ runtime·printpointer((void*)p[i]);
+ if(i%8 == 7)
+ runtime·prints("\n\t");
+ }
+ if(i%8)
+ runtime·prints("\n");
+ }
+
+ ret = mach_msg(h, MACH_SEND_MSG|MACH_RCV_MSG,
+ h->msgh_size, maxsize, port, 0, 0);
+ if(ret != 0){
+ if(DebugMach){
+ runtime·prints("mach_msg error ");
+ runtime·printint(ret);
+ runtime·prints("\n");
+ }
+ return ret;
+ }
+
+ if(DebugMach){
+ p = (uint32*)h;
+ runtime·prints("recv:\t");
+ for(i=0; i<h->msgh_size/sizeof(p[0]); i++){
+ runtime·prints(" ");
+ runtime·printpointer((void*)p[i]);
+ if(i%8 == 7)
+ runtime·prints("\n\t");
+ }
+ if(i%8)
+ runtime·prints("\n");
+ }
+
+ if(h->msgh_id != id+Reply){
+ if(DebugMach){
+ runtime·prints("mach_msg reply id mismatch ");
+ runtime·printint(h->msgh_id);
+ runtime·prints(" != ");
+ runtime·printint(id+Reply);
+ runtime·prints("\n");
+ }
+ return -303; // MIG_REPLY_MISMATCH
+ }
+
+ // Look for a response giving the return value.
+ // Any call can send this back with an error,
+ // and some calls only have return values so they
+ // send it back on success too. I don't quite see how
+ // you know it's one of these and not the full response
+ // format, so just look if the message is right.
+ c = (CodeMsg*)h;
+ if(h->msgh_size == sizeof(CodeMsg)
+ && !(h->msgh_bits & MACH_MSGH_BITS_COMPLEX)){
+ if(DebugMach){
+ runtime·prints("mig result ");
+ runtime·printint(c->code);
+ runtime·prints("\n");
+ }
+ return c->code;
+ }
+
+ if(h->msgh_size != rxsize){
+ if(DebugMach){
+ runtime·prints("mach_msg reply size mismatch ");
+ runtime·printint(h->msgh_size);
+ runtime·prints(" != ");
+ runtime·printint(rxsize);
+ runtime·prints("\n");
+ }
+ return -307; // MIG_ARRAY_TOO_LARGE
+ }
+
+ return 0;
+}
+
+
+// Semaphores!
+
+enum
+{
+ Tmach_semcreate = 3418,
+ Rmach_semcreate = Tmach_semcreate + Reply,
+
+ Tmach_semdestroy = 3419,
+ Rmach_semdestroy = Tmach_semdestroy + Reply,
+
+ // Mach calls that get interrupted by Unix signals
+ // return this error code. We retry them.
+ KERN_ABORTED = 14,
+};
+
+typedef struct Tmach_semcreateMsg Tmach_semcreateMsg;
+typedef struct Rmach_semcreateMsg Rmach_semcreateMsg;
+typedef struct Tmach_semdestroyMsg Tmach_semdestroyMsg;
+// Rmach_semdestroyMsg = CodeMsg
+
+#pragma pack on
+struct Tmach_semcreateMsg
+{
+ MachHeader h;
+ MachNDR ndr;
+ int32 policy;
+ int32 value;
+};
+
+struct Rmach_semcreateMsg
+{
+ MachHeader h;
+ MachBody body;
+ MachPort semaphore;
+};
+
+struct Tmach_semdestroyMsg
+{
+ MachHeader h;
+ MachBody body;
+ MachPort semaphore;
+};
+#pragma pack off
+
+uint32
+runtime·mach_semcreate(void)
+{
+ union {
+ Tmach_semcreateMsg tx;
+ Rmach_semcreateMsg rx;
+ uint8 pad[MinMachMsg];
+ } m;
+ int32 r;
+
+ m.tx.h.msgh_bits = 0;
+ m.tx.h.msgh_size = sizeof(m.tx);
+ m.tx.h.msgh_remote_port = runtime·mach_task_self();
+ m.tx.h.msgh_id = Tmach_semcreate;
+ m.tx.ndr = zerondr;
+
+ m.tx.policy = 0; // 0 = SYNC_POLICY_FIFO
+ m.tx.value = 0;
+
+ while((r = machcall(&m.tx.h, sizeof m, sizeof(m.rx))) != 0){
+ if(r == KERN_ABORTED) // interrupted
+ continue;
+ macherror(r, "semaphore_create");
+ }
+ if(m.rx.body.msgh_descriptor_count != 1)
+ unimplemented("mach_semcreate desc count");
+ return m.rx.semaphore.name;
+}
+
+void
+runtime·mach_semdestroy(uint32 sem)
+{
+ union {
+ Tmach_semdestroyMsg tx;
+ uint8 pad[MinMachMsg];
+ } m;
+ int32 r;
+
+ m.tx.h.msgh_bits = MACH_MSGH_BITS_COMPLEX;
+ m.tx.h.msgh_size = sizeof(m.tx);
+ m.tx.h.msgh_remote_port = runtime·mach_task_self();
+ m.tx.h.msgh_id = Tmach_semdestroy;
+ m.tx.body.msgh_descriptor_count = 1;
+ m.tx.semaphore.name = sem;
+ m.tx.semaphore.disposition = MACH_MSG_TYPE_MOVE_SEND;
+ m.tx.semaphore.type = 0;
+
+ while((r = machcall(&m.tx.h, sizeof m, 0)) != 0){
+ if(r == KERN_ABORTED) // interrupted
+ continue;
+ macherror(r, "semaphore_destroy");
+ }
+}
+
+// The other calls have simple system call traps in sys.s
+int32 runtime·mach_semaphore_wait(uint32 sema);
+int32 runtime·mach_semaphore_timedwait(uint32 sema, uint32 sec, uint32 nsec);
+int32 runtime·mach_semaphore_signal(uint32 sema);
+int32 runtime·mach_semaphore_signal_all(uint32 sema);
+
+void
+runtime·mach_semacquire(uint32 sem)
+{
+ int32 r;
+
+ while((r = runtime·mach_semaphore_wait(sem)) != 0) {
+ if(r == KERN_ABORTED) // interrupted
+ continue;
+ macherror(r, "semaphore_wait");
+ }
+}
+
+void
+runtime·mach_semrelease(uint32 sem)
+{
+ int32 r;
+
+ while((r = runtime·mach_semaphore_signal(sem)) != 0) {
+ if(r == KERN_ABORTED) // interrupted
+ continue;
+ macherror(r, "semaphore_signal");
+ }
+}
+
+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);
+}