]> Cypherpunks repositories - gostls13.git/commitdiff
go threads for OS X
authorRuss Cox <rsc@golang.org>
Tue, 9 Sep 2008 18:50:14 +0000 (11:50 -0700)
committerRuss Cox <rsc@golang.org>
Tue, 9 Sep 2008 18:50:14 +0000 (11:50 -0700)
R=r
OCL=14944
CL=15013

src/runtime/amd64_darwin.h
src/runtime/amd64_linux.h
src/runtime/chan.c
src/runtime/proc.c
src/runtime/rt0_amd64.s
src/runtime/rt1_amd64_darwin.c
src/runtime/rt1_amd64_linux.c
src/runtime/runtime.h
src/runtime/sys_amd64_darwin.s
src/runtime/sys_amd64_linux.s

index 18d1b605fccd5cc7148d5865d4fd7ecfc6435284..117c985a1d5e493a67622ece1e6fae7bd274e775 100644 (file)
@@ -22,6 +22,11 @@ struct timespec {
        int64 tv_nsec;
 };
 
+struct timeval {
+       time_t tv_sec;
+       int64 tv_usec;
+};
+
 struct stat {  // really a stat64
        dev_t st_dev;
        mode_t st_mode;
@@ -43,3 +48,19 @@ struct stat {        // really a stat64
 };
 
 #define        O_CREAT 0x0200
+
+void bsdthread_create(void*, M*, G*, void(*)(void));
+void bsdthread_register(void);
+int64 select(int32, void*, void*, void*, struct timeval*);
+
+
+// Mach calls
+
+typedef int32 kern_return_t;
+typedef uint32 mach_port_t;
+
+mach_port_t semcreate(void);
+void semacquire(mach_port_t);
+void semrelease(mach_port_t);
+void semreset(mach_port_t);
+void semdestroy(mach_port_t);
index 4ec4f85e7a196d4991c6d75f07c2532a8cfd810c..fc70514ff0f7b48f650c94cf24cd6a3b7d028565 100644 (file)
@@ -49,5 +49,5 @@ struct stat {
 // Linux-specific system calls
 int64  futex(uint32*, int32, uint32, struct timespec*, uint32*, uint32);
 int64  clone(int32, void*, M*, G*, void(*)(void));
-int64  select(int32, void*, void*, void*, void*);
+int64  select(int32, void*, void*, void*, struct timeval*);
 
index be7812c9d4ecb123e7513bc0208a92696d074f48..ffbab902f487568279dd7e2a60a08e632b612664 100644 (file)
@@ -4,9 +4,8 @@
 
 #include "runtime.h"
 
-// TODO locking of select
-
 static int32   debug   = 0;
+static Lock            chanlock;
 
 typedef        struct  Hchan   Hchan;
 typedef        struct  Link    Link;
@@ -32,7 +31,6 @@ struct        WaitQ
 
 struct Hchan
 {
-       Lock;
        uint32  elemsize;
        uint32  dataqsiz;               // size of the circular q
        uint32  qcount;                 // total data in the q
@@ -162,7 +160,7 @@ sendchan(Hchan *c, byte *ep, bool *pres)
                prints("\n");
        }
 
-       lock(c);
+       lock(&chanlock);
        if(c->dataqsiz > 0)
                goto asynch;
 
@@ -173,7 +171,7 @@ sendchan(Hchan *c, byte *ep, bool *pres)
 
                gp = sg->g;
                gp->param = sg;
-               unlock(c);
+               unlock(&chanlock);
                ready(gp);
 
                if(pres != nil)
@@ -182,7 +180,7 @@ sendchan(Hchan *c, byte *ep, bool *pres)
        }
 
        if(pres != nil) {
-               unlock(c);
+               unlock(&chanlock);
                *pres = false;
                return;
        }
@@ -193,13 +191,13 @@ sendchan(Hchan *c, byte *ep, bool *pres)
        g->param = nil;
        g->status = Gwaiting;
        enqueue(&c->sendq, sg);
-       unlock(c);
+       unlock(&chanlock);
        sys·gosched();
 
-       lock(c);
+       lock(&chanlock);
        sg = g->param;
        freesg(c, sg);
-       unlock(c);
+       unlock(&chanlock);
        return;
 
 asynch:
@@ -208,9 +206,9 @@ asynch:
                sg = allocsg(c);
                g->status = Gwaiting;
                enqueue(&c->sendq, sg);
-               unlock(c);
+               unlock(&chanlock);
                sys·gosched();
-               lock(c);
+               lock(&chanlock);
        }
        if(ep != nil)
                c->elemalg->copy(c->elemsize, c->senddataq->elem, ep);
@@ -221,10 +219,10 @@ asynch:
        if(sg != nil) {
                gp = sg->g;
                freesg(c, sg);
-               unlock(c);
+               unlock(&chanlock);
                ready(gp);
        }else
-               unlock(c);
+               unlock(&chanlock);
 }
 
 static void
@@ -239,7 +237,7 @@ chanrecv(Hchan* c, byte *ep, bool* pres)
                prints("\n");
        }
 
-       lock(c);
+       lock(&chanlock);
        if(c->dataqsiz > 0)
                goto asynch;
 
@@ -249,7 +247,7 @@ chanrecv(Hchan* c, byte *ep, bool* pres)
 
                gp = sg->g;
                gp->param = sg;
-               unlock(c);
+               unlock(&chanlock);
                ready(gp);
 
                if(pres != nil)
@@ -258,7 +256,7 @@ chanrecv(Hchan* c, byte *ep, bool* pres)
        }
 
        if(pres != nil) {
-               unlock(c);
+               unlock(&chanlock);
                *pres = false;
                return;
        }
@@ -267,14 +265,14 @@ chanrecv(Hchan* c, byte *ep, bool* pres)
        g->param = nil;
        g->status = Gwaiting;
        enqueue(&c->recvq, sg);
-       unlock(c);
+       unlock(&chanlock);
        sys·gosched();
 
-       lock(c);
+       lock(&chanlock);
        sg = g->param;
        c->elemalg->copy(c->elemsize, ep, sg->elem);
        freesg(c, sg);
-       unlock(c);
+       unlock(&chanlock);
        return;
 
 asynch:
@@ -282,9 +280,9 @@ asynch:
                sg = allocsg(c);
                g->status = Gwaiting;
                enqueue(&c->recvq, sg);
-               unlock(c);
+               unlock(&chanlock);
                sys·gosched();
-               lock(c);
+               lock(&chanlock);
        }
        c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem);
        c->recvdataq = c->recvdataq->link;
@@ -293,10 +291,10 @@ asynch:
        if(sg != nil) {
                gp = sg->g;
                freesg(c, sg);
-               unlock(c);
+               unlock(&chanlock);
                ready(gp);
        }else
-               unlock(c);
+               unlock(&chanlock);
 }
 
 // chansend1(hchan *chan any, elem any);
@@ -371,12 +369,14 @@ sys·newselect(int32 size, Select *sel)
        if(size > 1)
                n = size-1;
 
+       lock(&chanlock);
        sel = nil;
        if(size >= 1 && size < nelem(selfree)) {
                sel = selfree[size];
                if(sel != nil)
                        selfree[size] = sel->link;
        }
+       unlock(&chanlock);
        if(sel == nil)
                sel = mal(sizeof(*sel) + n*sizeof(sel->scase[0]));
 
@@ -517,6 +517,8 @@ sys·selectgo(Select *sel)
        p %= sel->ncase;
        o %= sel->ncase;
 
+       lock(&chanlock);
+
        // pass 1 - look for something already waiting
        for(i=0; i<sel->ncase; i++) {
                cas = &sel->scase[o];
@@ -598,8 +600,10 @@ sys·selectgo(Select *sel)
        // (rsc) not correct to set Gwaiting after queueing;
        // might already have been readied.
        g->status = Gwaiting;
+       unlock(&chanlock);
        sys·gosched();
 
+       lock(&chanlock);
        sg = g->param;
        o = sg->offset;
        cas = &sel->scase[o];
@@ -629,6 +633,7 @@ sys·selectgo(Select *sel)
 
 asynr:
 asyns:
+       unlock(&chanlock);
        throw("asyn");
        return; // compiler doesn't know throw doesn't return
 
@@ -671,6 +676,7 @@ retc:
                sel->link = selfree[sel->ncase];
                selfree[sel->ncase] = sel;
        }
+       unlock(&chanlock);
 
        sys·setcallerpc(&sel, cas->pc);
        as = (byte*)&sel + cas->so;
index 5b4bc84a223b7e53378ba7e5301bab2043e9b52f..6a741f8822ba9a0ac3b11c63988065a60da06ae9 100644 (file)
@@ -341,8 +341,6 @@ scheduler(void)
 {
        G* gp;
 
-       // Initialization.
-       m->procid = getprocid();
        lock(&sched);
 
        if(gosave(&m->sched)){
@@ -472,7 +470,7 @@ oldstack(void)
                mcpy(top->oldsp+16, sp, siz2);
        }
 
-       // call  no more functions after this point - limit register disagrees with R15
+       // call  no more functions after this point - stackguard disagrees with SP
        m->curg->stackbase = top->oldbase;
        m->curg->stackguard = top->oldguard;
        m->morestack.SP = top->oldsp+8;
index 2898da56b479139a239d3633557056b862406622..0200f35f481a09b837eb05817a439b98bbd97490 100644 (file)
@@ -33,6 +33,7 @@ TEXT  _rt0_amd64(SB),7,$-8
        MOVQ    24(SP), AX              // copy argv
        MOVQ    AX, 8(SP)
        CALL    args(SB)
+       CALL    osinit(SB)
        CALL    schedinit(SB)
        CALL    main·init_function(SB) // initialization
 
index e99d07984bbf64158cf3c6b6211d05d21f99368e..da10508a0e9bf10f7de2a8913aa75f8cd93c9117 100644 (file)
@@ -3,6 +3,7 @@
 // license that can be found in the LICENSE file.
 
 #include "runtime.h"
+#include "amd64_darwin.h"
 #include "signals.h"
 
 typedef uint64 __uint64_t;
@@ -185,53 +186,494 @@ unimplemented(int8 *name)
 void
 sys·sleep(int64 ms)
 {
-       unimplemented("sleep");
+       struct timeval tv;
+
+       tv.tv_sec = ms/1000;
+       tv.tv_usec = ms%1000 * 1000;
+       select(0, nil, nil, nil, &tv);
+}
+
+// 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 = semcreate();
+       if(!cas(psema, 0, sema)){
+               // Someone else filled it in.  Use theirs.
+               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-space 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(cas(&l->key, 0, 1))
-               return;
-       unimplemented("lock wait");
+       // Allocate semaphore if needed.
+       if(l->sema == 0)
+               initsema(&l->sema);
+
+       if(xadd(&l->key, 1) > 1)        // someone else has it; wait
+               semacquire(l->sema);
 }
 
 void
 unlock(Lock *l)
 {
-       if(cas(&l->key, 1, 0))
-               return;
-       unimplemented("unlock wakeup");
+       if(xadd(&l->key, -1) > 0)       // someone else is waiting
+               semrelease(l->sema);
 }
 
+
+// Event notifications.
 void
 noteclear(Note *n)
 {
-       n->lock.key = 0;
-       lock(&n->lock);
+       n->wakeup = 0;
 }
 
 void
 notesleep(Note *n)
 {
-       lock(&n->lock);
-       unlock(&n->lock);
+       if(n->sema == 0)
+               initsema(&n->sema);
+       while(!n->wakeup)
+               semacquire(n->sema);
 }
 
 void
 notewakeup(Note *n)
 {
-       unlock(&n->lock);
+       if(n->sema == 0)
+               initsema(&n->sema);
+       n->wakeup = 1;
+       semrelease(n->sema);
 }
 
+
+// BSD interface for threading.
 void
-newosproc(M *mm, G *gg, void *stk, void (*fn)(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);
+}
+
+
+// Mach IPC, to get at semaphores
+// Definitions are in /usr/include/mach on a Mac.
+
+static void
+macherror(kern_return_t r, int8 *fn)
 {
-       unimplemented("newosproc");
+       prints("mach error ");
+       prints(fn);
+       prints(": ");
+       sys·printint(r);
+       prints("\n");
+       throw("mach error");
 }
 
-int32
-getprocid(void)
+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
+{
+       Tsemcreate = 3418,
+       Rsemcreate = Tsemcreate + Reply,
+
+       Tsemdestroy = 3419,
+       Rsemdestroy = Tsemdestroy + Reply,
+};
+
+typedef struct TsemcreateMsg TsemcreateMsg;
+typedef struct RsemcreateMsg RsemcreateMsg;
+typedef struct TsemdestroyMsg TsemdestroyMsg;
+// RsemdestroyMsg = CodeMsg
+
+#pragma pack on
+struct TsemcreateMsg
+{
+       mach_msg_header_t h;
+       NDR_record_t ndr;
+       int32 policy;
+       int32 value;
+};
+
+struct RsemcreateMsg
+{
+       mach_msg_header_t h;
+       mach_msg_body_t body;
+       mach_msg_port_descriptor_t semaphore;
+};
+
+struct TsemdestroyMsg
+{
+       mach_msg_header_t h;
+       mach_msg_body_t body;
+       mach_msg_port_descriptor_t semaphore;
+};
+#pragma pack off
+
+mach_port_t
+semcreate(void)
+{
+       union {
+               TsemcreateMsg tx;
+               RsemcreateMsg 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 = Tsemcreate;
+       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("semcreate desc count");
+       return m.rx.semaphore.name;
+}
+
+void
+semdestroy(mach_port_t sem)
+{
+       union {
+               TsemdestroyMsg 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 = Tsemdestroy;
+       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
+semacquire(mach_port_t sem)
+{
+       kern_return_t r;
+
+       if((r = mach_semaphore_wait(sem)) != 0)
+               macherror(r, "semaphore_wait");
+}
+
+void
+semrelease(mach_port_t sem)
+{
+       kern_return_t r;
+
+       if((r = mach_semaphore_signal(sem)) != 0)
+               macherror(r, "semaphore_signal");
+}
+
index baff4889c9879ccb2ff3f22472398fba364b7e04..c14b3385256dc69e55a47e8b34e59c4f574abe87 100644 (file)
@@ -427,3 +427,7 @@ sys·sleep(int64 ms)
        select(0, nil, nil, nil, &tv);
 }
 
+void
+osinit(void)
+{
+}
index c7471cf3e6d4ea5dbda894d60f05f09d6ebc15cd..c64e5b37e7dd23af3703ade4276bfb94a34bca57 100644 (file)
@@ -44,7 +44,7 @@ typedef       struct  M               M;
 typedef        struct  Stktop          Stktop;
 typedef        struct  Alg             Alg;
 typedef        struct  Lock            Lock;
-typedef        struct  Note    Note;
+typedef        union   Note    Note;
 typedef        struct  Mem             Mem;
 
 /*
@@ -78,10 +78,17 @@ enum
 struct Lock
 {
        uint32  key;
+       uint32  sema;   // for OS X
 };
-struct Note
+union  Note
 {
-       Lock    lock;
+       struct {        // Linux
+               Lock    lock;
+       };
+       struct {        // OS X
+               int32   wakeup;
+               uint32  sema;
+       };
 };
 struct String
 {
@@ -149,6 +156,7 @@ struct      M
        G*      g0;             // g0 w interrupt stack - must not move
        uint64  morearg;        // arg to morestack - must not move
        uint64  cret;           // return value from C - must not move
+       uint64  procid; // for debuggers - must not move
        G*      curg;           // current running goroutine
        G*      lastg;          // last running goroutine - to emulate fifo
        Gobuf   sched;
@@ -159,8 +167,8 @@ struct      M
        Note    havenextg;
        G*      nextg;
        M*      schedlink;
-       int32   procid; // for debuggers
        Mem     mem;
+       uint32  machport;       // Return address for Mach IPC (OS X)
 };
 struct Stktop
 {
@@ -239,7 +247,6 @@ void        ready(G*);
 byte*  getenv(int8*);
 int32  atoi(byte*);
 void   newosproc(M *m, G *g, void *stk, void (*fn)(void));
-int32  getprocid(void);
 
 /*
  * mutual exclusion locks.  in the uncontended case,
index 319c8a4dd99a2ac6b23a7e007a093795bf92e6df..19d8184efbf006fab24e61ea8fda94f1b0618581 100644 (file)
@@ -4,12 +4,11 @@
 
 //
 // System calls and other sys.stuff for AMD64, Darwin
+// See http://fxr.watson.org/fxr/source/bsd/kern/syscalls.c?v=xnu-1228
+// or /usr/include/sys/syscall.h (on a Mac) for system call numbers.
 //
 
-// TODO(rsc): Either sys·exit or exit1 is wrong!
-// It looks like sys·exit is correct (exits the entire program)
-// and exit1 should be mimicking the OS X library routine
-// __bsdthread_terminate.
+// Exit the entire program (like C exit)
 TEXT   sys·exit(SB),7,$-8
        MOVL    8(SP), DI               // arg 1 exit status
        MOVL    $(0x2000000+1), AX      // syscall entry
@@ -17,9 +16,11 @@ TEXT sys·exit(SB),7,$-8
        CALL    notok(SB)
        RET
 
+// Exit this OS thread (like pthread_exit, which eventually
+// calls __bsdthread_terminate).
 TEXT   exit1(SB),7,$-8
        MOVL    8(SP), DI               // arg 1 exit status
-       MOVL    $(0x2000000+1), AX      // syscall entry
+       MOVL    $(0x2000000+361), AX    // syscall entry
        SYSCALL
        CALL    notok(SB)
        RET
@@ -130,3 +131,127 @@ TEXT      sys·setcallerpc+0(SB),7,$0
        MOVQ    x+8(FP), BX
        MOVQ    BX, -8(AX)              // set calling pc
        RET
+
+// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void))
+TEXT bsdthread_create(SB),7,$-8
+       // Set up arguments to bsdthread_create system call.
+       // The ones in quotes pass through to the thread callback
+       // uninterpreted, so we can put whatever we want there.
+       MOVQ    fn+32(SP), DI   // "func"
+       MOVQ    m+16(SP), SI    // "arg"
+       MOVQ    stk+8(SP), DX   // stack
+       MOVQ    g+24(SP), R10   // "pthread"
+       MOVQ    $0, R10 // flags
+       MOVQ    $(0x2000000+360), AX    // bsdthread_create
+       SYSCALL
+       JCC 2(PC)
+       CALL    notok(SB)
+       RET
+
+// The thread that bsdthread_create creates starts executing here,
+// because we registered this function using bsdthread_register
+// at startup.
+//     DI = "pthread" (= g)
+//     SI = mach thread port
+//     DX = "func" (= fn)
+//     CX = "arg" (= m)
+//     R8 = stack
+//     R9 = flags (= 0)
+//     SP = stack - C_64_REDZONE_LEN (= stack - 128)
+TEXT bsdthread_start(SB),7,$-8
+       MOVQ    CX, R14 // m
+       MOVQ    DI, R15 // g
+       MOVQ    SI, 24(R14)     // thread port is m->procid
+       CALL    DX      // fn
+       CALL    exit1(SB)
+       RET
+
+// void bsdthread_register(void)
+// registers callbacks for threadstart (see bsdthread_create above
+// and wqthread and pthsize (not used).  returns 0 on success.
+TEXT bsdthread_register(SB),7,$-8
+       MOVQ    $bsdthread_start(SB), DI        // threadstart
+       MOVQ    $0, SI  // wqthread, not used by us
+       MOVQ    $0, DX  // pthsize, not used by us
+       MOVQ    $(0x2000000+366), AX    // bsdthread_register
+       SYSCALL
+       JCC 2(PC)
+       CALL    notok(SB)
+       RET
+
+// int64 select(int32, void*, void*, void*, void*)
+TEXT select(SB),7,$0
+       MOVL    8(SP), DI
+       MOVQ    16(SP), SI
+       MOVQ    24(SP), DX
+       MOVQ    32(SP), R10
+       MOVQ    40(SP), R8
+       MOVL    $(0x2000000+407), AX    // select_nocancel
+       SYSCALL
+       RET
+
+// Mach system calls use 0x1000000 instead of the BSD's 0x2000000.
+
+// uint32 mach_msg_trap(void*, uint32, uint32, uint32, uint32, uint32, uint32)
+TEXT mach_msg_trap(SB),7,$0
+       MOVQ    8(SP), DI
+       MOVL    16(SP), SI
+       MOVL    20(SP), DX
+       MOVL    24(SP), R10
+       MOVL    28(SP), R8
+       MOVL    32(SP), R9
+       MOVL    36(SP), R11
+       PUSHQ   R11     // seventh arg, on stack
+       MOVL    $(0x1000000+31), AX     // mach_msg_trap
+       SYSCALL
+       POPQ    R11
+       RET
+
+TEXT mach_task_self(SB),7,$0
+       MOVL    $(0x1000000+28), AX     // task_self_trap
+       SYSCALL
+       RET
+
+TEXT mach_thread_self(SB),7,$0
+       MOVL    $(0x1000000+27), AX     // thread_self_trap
+       SYSCALL
+       RET
+
+TEXT mach_reply_port(SB),7,$0
+       MOVL    $(0x1000000+26), AX     // mach_reply_port
+       SYSCALL
+       RET
+
+// Mach provides trap versions of the semaphore ops,
+// instead of requiring the use of RPC.
+
+// uint32 mach_semaphore_wait(uint32)
+TEXT mach_semaphore_wait(SB),7,$0
+       MOVL    8(SP), DI
+       MOVL    $(0x1000000+36), AX     // semaphore_wait_trap
+       SYSCALL
+       RET
+
+// uint32 mach_semaphore_timedwait(uint32, uint32, uint32)
+TEXT mach_semaphore_timedwait(SB),7,$0
+       MOVL    8(SP), DI
+       MOVL    12(SP), SI
+       MOVL    16(SP), DX
+       MOVL    $(0x1000000+38), AX     // semaphore_timedwait_trap
+       SYSCALL
+       RET
+
+// uint32 mach_semaphore_signal(uint32)
+TEXT mach_semaphore_signal(SB),7,$0
+       MOVL    8(SP), DI
+       MOVL    $(0x1000000+33), AX     // semaphore_signal_trap
+       SYSCALL
+       RET
+
+// uint32 mach_semaphore_signal_all(uint32)
+TEXT mach_semaphore_signal_all(SB),7,$0
+       MOVL    8(SP), DI
+       MOVL    $(0x1000000+34), AX     // semaphore_signal_all_trap
+       SYSCALL
+       RET
+
index 2cb62583200b89b65db814cf0fa4c98a4125304f..01f6f6280d8c7b8f625642fd0cdef0e0a20be0e5 100644 (file)
@@ -162,10 +162,17 @@ TEXT clone(SB),7,$0
        JEQ     2(PC)
        RET
 
-       // In child, call fn on new stack
+       // In child, set up new stack
        MOVQ    SI, SP
        MOVQ    R8, R14 // m
        MOVQ    R9, R15 // g
+       
+       // Initialize m->procid to Linux tid
+       MOVL    $186, AX        // gettid
+       SYSCALL
+       MOVQ    AX, 24(R14)
+       
+       // Call fn
        CALL    R12
 
        // It shouldn't return.  If it does, exi
@@ -174,7 +181,7 @@ TEXT clone(SB),7,$0
        SYSCALL
        JMP     -3(PC)  // keep exiting
 
-// int64 select(int32, void*, void*, void*, void*)
+// int64 select(int32, void*, void*, void*, struct timeval*)
 TEXT select(SB),7,$0
        MOVL    8(SP), DI
        MOVQ    16(SP), SI
@@ -185,16 +192,3 @@ TEXT select(SB),7,$0
        SYSCALL
        RET
 
-// Linux allocates each thread its own pid, like Plan 9.
-// But the getpid() system call returns the pid of the
-// original thread (the one that exec started with),
-// no matter which thread asks.  This system call,
-// which Linux calls gettid, returns the actual pid of
-// the calling thread, not the fake one.
-//
-// int32 getprocid(void)
-TEXT getprocid(SB),7,$0
-       MOVL    $186, AX
-       SYSCALL
-       RET
-