diff options
author | Russ Cox <rsc@golang.org> | 2009-03-24 13:17:10 -0700 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2009-03-24 13:17:10 -0700 |
commit | b6c7cd371db650c8573629d17f8b4fdad254029b (patch) | |
tree | 77c5d566b29a6954bf41f41634770ed895bc3794 /src/runtime/darwin/thread.c | |
parent | 088b4dbf96402e0b32d24ee7f4a530ab5da79040 (diff) | |
download | golang-b6c7cd371db650c8573629d17f8b4fdad254029b.tar.gz |
split rt1.c into signal.c and thread.c.
move out of arch-specific directory: only os-specific.
rm sys_types.h (unused).
TBR=r
OCL=26681
CL=26681
Diffstat (limited to 'src/runtime/darwin/thread.c')
-rw-r--r-- | src/runtime/darwin/thread.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/src/runtime/darwin/thread.c b/src/runtime/darwin/thread.c new file mode 100644 index 000000000..06bbde5ab --- /dev/null +++ b/src/runtime/darwin/thread.c @@ -0,0 +1,535 @@ +// 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" + +static void +unimplemented(int8 *name) +{ + prints(name); + 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 = mach_semcreate(); + if(!cas(psema, 0, sema)){ + // Someone else filled it in. Use theirs. + mach_semdestroy(sema); + return; + } +} + + +// Atomic add and return new value. +static uint32 +xadd(uint32 volatile *val, int32 delta) +{ + uint32 oval, nval; + + for(;;){ + oval = *val; + nval = oval + delta; + if(cas(val, oval, nval)) + return nval; + } +} + + +// 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. +// +// Note that semaphores are never destroyed (the kernel +// will clean up when the process exits). We assume for now +// that Locks are only used for long-lived structures like M and G. + +void +lock(Lock *l) +{ + if(m->locks < 0) + throw("lock count"); + m->locks++; + + // Allocate semaphore if needed. + if(l->sema == 0) + initsema(&l->sema); + + if(xadd(&l->key, 1) > 1) // someone else has it; wait + mach_semacquire(l->sema); +} + +void +unlock(Lock *l) +{ + m->locks--; + if(m->locks < 0) + throw("lock count"); + + if(xadd(&l->key, -1) > 0) // someone else is waiting + mach_semrelease(l->sema); +} + + +// 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 +usemacquire(Usema *s) +{ + if((int32)xadd(&s->u, -1) < 0) + mach_semacquire(s->k); +} + +void +usemrelease(Usema *s) +{ + if((int32)xadd(&s->u, 1) <= 0) + mach_semrelease(s->k); +} + + +// Event notifications. +void +noteclear(Note *n) +{ + n->wakeup = 0; +} + +void +notesleep(Note *n) +{ + if(n->sema.k == 0) + initsema(&n->sema.k); + while(!n->wakeup) + usemacquire(&n->sema); +} + +void +notewakeup(Note *n) +{ + if(n->sema.k == 0) + initsema(&n->sema.k); + n->wakeup = 1; + usemrelease(&n->sema); +} + + +// BSD interface for threading. +void +osinit(void) +{ + // Register our thread-creation callback (see sys_amd64_darwin.s). + bsdthread_register(); +} + +void +newosproc(M *m, G *g, void *stk, void (*fn)(void)) +{ + bsdthread_create(stk, m, g, fn); +} + +// Called to initialize a new m (including the bootstrap m). +void +minit(void) +{ + // Initialize signal handling. + m->gsignal = malg(32*1024); // OS X wants >=8K, Linux >=2K + signalstack(m->gsignal->stackguard, 32*1024); +} + + +// Mach IPC, to get at semaphores +// Definitions are in /usr/include/mach on a Mac. + +static void +macherror(kern_return_t r, int8 *fn) +{ + prints("mach error "); + prints(fn); + prints(": "); + sys·printint(r); + prints("\n"); + throw("mach error"); +} + +enum +{ + DebugMach = 0 +}; + +typedef int32 mach_msg_option_t; +typedef uint32 mach_msg_bits_t; +typedef uint32 mach_msg_id_t; +typedef uint32 mach_msg_size_t; +typedef uint32 mach_msg_timeout_t; +typedef uint32 mach_port_name_t; +typedef uint64 mach_vm_address_t; + +typedef struct mach_msg_header_t mach_msg_header_t; +typedef struct mach_msg_body_t mach_msg_body_t; +typedef struct mach_msg_port_descriptor_t mach_msg_port_descriptor_t; +typedef struct NDR_record_t NDR_record_t; + +enum +{ + MACH_MSG_TYPE_MOVE_RECEIVE = 16, + MACH_MSG_TYPE_MOVE_SEND = 17, + MACH_MSG_TYPE_MOVE_SEND_ONCE = 18, + MACH_MSG_TYPE_COPY_SEND = 19, + MACH_MSG_TYPE_MAKE_SEND = 20, + MACH_MSG_TYPE_MAKE_SEND_ONCE = 21, + MACH_MSG_TYPE_COPY_RECEIVE = 22, + + MACH_MSG_PORT_DESCRIPTOR = 0, + MACH_MSG_OOL_DESCRIPTOR = 1, + MACH_MSG_OOL_PORTS_DESCRIPTOR = 2, + MACH_MSG_OOL_VOLATILE_DESCRIPTOR = 3, + + MACH_MSGH_BITS_COMPLEX = 0x80000000, + + MACH_SEND_MSG = 1, + MACH_RCV_MSG = 2, + MACH_RCV_LARGE = 4, + + MACH_SEND_TIMEOUT = 0x10, + MACH_SEND_INTERRUPT = 0x40, + MACH_SEND_CANCEL = 0x80, + MACH_SEND_ALWAYS = 0x10000, + MACH_SEND_TRAILER = 0x20000, + MACH_RCV_TIMEOUT = 0x100, + MACH_RCV_NOTIFY = 0x200, + MACH_RCV_INTERRUPT = 0x400, + MACH_RCV_OVERWRITE = 0x1000, +}; + +mach_port_t mach_task_self(void); +mach_port_t mach_thread_self(void); + +#pragma pack on +struct mach_msg_header_t +{ + mach_msg_bits_t bits; + mach_msg_size_t size; + mach_port_t remote_port; + mach_port_t local_port; + mach_msg_size_t reserved; + mach_msg_id_t id; +}; + +struct mach_msg_body_t +{ + uint32 descriptor_count; +}; + +struct mach_msg_port_descriptor_t +{ + mach_port_t name; + uint32 pad1; + uint16 pad2; + uint8 disposition; + uint8 type; +}; + +enum +{ + NDR_PROTOCOL_2_0 = 0, + NDR_INT_BIG_ENDIAN = 0, + NDR_INT_LITTLE_ENDIAN = 1, + NDR_FLOAT_IEEE = 0, + NDR_CHAR_ASCII = 0 +}; + +struct NDR_record_t +{ + uint8 mig_vers; + uint8 if_vers; + uint8 reserved1; + uint8 mig_encoding; + uint8 int_rep; + uint8 char_rep; + uint8 float_rep; + uint8 reserved2; +}; +#pragma pack off + +static NDR_record_t zerondr; + +#define MACH_MSGH_BITS(a, b) ((a) | ((b)<<8)) + +// Mach system calls (in sys_amd64_darwin.s) +kern_return_t mach_msg_trap(mach_msg_header_t*, + mach_msg_option_t, mach_msg_size_t, mach_msg_size_t, + mach_port_name_t, mach_msg_timeout_t, mach_port_name_t); +mach_port_t mach_reply_port(void); +mach_port_t mach_task_self(void); +mach_port_t mach_thread_self(void); + +static kern_return_t +mach_msg(mach_msg_header_t *h, + mach_msg_option_t op, + mach_msg_size_t send_size, + mach_msg_size_t rcv_size, + mach_port_name_t rcv_name, + mach_msg_timeout_t timeout, + mach_port_name_t notify) +{ + // TODO: Loop on interrupt. + return mach_msg_trap(h, op, send_size, rcv_size, rcv_name, timeout, notify); +} + + +// Mach RPC (MIG) +// I'm not using the Mach names anymore. They're too long. + +enum +{ + MinMachMsg = 48, + Reply = 100, +}; + +#pragma pack on +typedef struct CodeMsg CodeMsg; +struct CodeMsg +{ + mach_msg_header_t h; + NDR_record_t NDR; + kern_return_t code; +}; +#pragma pack off + +static kern_return_t +machcall(mach_msg_header_t *h, int32 maxsize, int32 rxsize) +{ + uint32 *p; + int32 i, ret, id; + mach_port_t port; + CodeMsg *c; + + if((port = m->machport) == 0){ + port = mach_reply_port(); + m->machport = port; + } + + h->bits |= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + h->local_port = port; + h->reserved = 0; + id = h->id; + + if(DebugMach){ + p = (uint32*)h; + prints("send:\t"); + for(i=0; i<h->size/sizeof(p[0]); i++){ + prints(" "); + sys·printpointer((void*)p[i]); + if(i%8 == 7) + prints("\n\t"); + } + if(i%8) + prints("\n"); + } + + ret = mach_msg(h, MACH_SEND_MSG|MACH_RCV_MSG, + h->size, maxsize, port, 0, 0); + if(ret != 0){ + if(DebugMach){ + prints("mach_msg error "); + sys·printint(ret); + prints("\n"); + } + return ret; + } + + if(DebugMach){ + p = (uint32*)h; + prints("recv:\t"); + for(i=0; i<h->size/sizeof(p[0]); i++){ + prints(" "); + sys·printpointer((void*)p[i]); + if(i%8 == 7) + prints("\n\t"); + } + if(i%8) + prints("\n"); + } + + if(h->id != id+Reply){ + if(DebugMach){ + prints("mach_msg reply id mismatch "); + sys·printint(h->id); + prints(" != "); + sys·printint(id+Reply); + 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->size == sizeof(CodeMsg) + && !(h->bits & MACH_MSGH_BITS_COMPLEX)){ + if(DebugMach){ + prints("mig result "); + sys·printint(c->code); + prints("\n"); + } + return c->code; + } + + if(h->size != rxsize){ + if(DebugMach){ + prints("mach_msg reply size mismatch "); + sys·printint(h->size); + prints(" != "); + sys·printint(rxsize); + 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, +}; + +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 +{ + mach_msg_header_t h; + NDR_record_t ndr; + int32 policy; + int32 value; +}; + +struct Rmach_semcreateMsg +{ + mach_msg_header_t h; + mach_msg_body_t body; + mach_msg_port_descriptor_t semaphore; +}; + +struct Tmach_semdestroyMsg +{ + mach_msg_header_t h; + mach_msg_body_t body; + mach_msg_port_descriptor_t semaphore; +}; +#pragma pack off + +mach_port_t +mach_semcreate(void) +{ + union { + Tmach_semcreateMsg tx; + Rmach_semcreateMsg rx; + uint8 pad[MinMachMsg]; + } m; + kern_return_t r; + + m.tx.h.bits = 0; + m.tx.h.size = sizeof(m.tx); + m.tx.h.remote_port = mach_task_self(); + m.tx.h.id = Tmach_semcreate; + m.tx.ndr = zerondr; + + m.tx.policy = 0; // 0 = SYNC_POLICY_FIFO + m.tx.value = 0; + + if((r = machcall(&m.tx.h, sizeof m, sizeof(m.rx))) != 0) + macherror(r, "semaphore_create"); + if(m.rx.body.descriptor_count != 1) + unimplemented("mach_semcreate desc count"); + return m.rx.semaphore.name; +} + +void +mach_semdestroy(mach_port_t sem) +{ + union { + Tmach_semdestroyMsg tx; + uint8 pad[MinMachMsg]; + } m; + kern_return_t r; + + m.tx.h.bits = MACH_MSGH_BITS_COMPLEX; + m.tx.h.size = sizeof(m.tx); + m.tx.h.remote_port = mach_task_self(); + m.tx.h.id = Tmach_semdestroy; + m.tx.body.descriptor_count = 1; + m.tx.semaphore.name = sem; + m.tx.semaphore.disposition = MACH_MSG_TYPE_MOVE_SEND; + m.tx.semaphore.type = 0; + + if((r = machcall(&m.tx.h, sizeof m, 0)) != 0) + macherror(r, "semaphore_destroy"); +} + +// The other calls have simple system call traps +// in sys_amd64_darwin.s +kern_return_t mach_semaphore_wait(uint32 sema); +kern_return_t mach_semaphore_timedwait(uint32 sema, uint32 sec, uint32 nsec); +kern_return_t mach_semaphore_signal(uint32 sema); +kern_return_t mach_semaphore_signal_all(uint32 sema); + +void +mach_semacquire(mach_port_t sem) +{ + kern_return_t r; + + if((r = mach_semaphore_wait(sem)) != 0) + macherror(r, "semaphore_wait"); +} + +void +mach_semrelease(mach_port_t sem) +{ + kern_return_t r; + + if((r = mach_semaphore_signal(sem)) != 0) + macherror(r, "semaphore_signal"); +} + |