1
0
mirror of https://github.com/golang/go synced 2024-10-05 04:21:22 -06:00
go/src/pkg/runtime/darwin/thread.c
Russ Cox 820dc9ff1a runtime: fix signal stack bug
In CL 4188061 I changed malg to allocate the requested
number of bytes n, not n+StackGuard, so that the
allocations would use rounder numbers.

The allocation of the signal stack asks for 32k and
then used g->stackguard as the base, but g->stackguard
is StackGuard bytes above the base.  Previously, asking
for 32k meant getting 32k+StackGuard bytes, so using
g->stackguard as the base was safe.  Now, the actual base
must be computed, so that the signal handler does not
run StackGuard bytes past the top of the stack.

Was causing flakiness mainly in programs that use the
network, because they sometimes write to closed network
connections, causing SIGPIPEs.  Was also causing problems
in the doc/progs test.

Also fix Makefile so that changes to stack.h trigger rebuild.

R=bradfitzgo, r, r2
CC=golang-dev
https://golang.org/cl/4230044
2011-02-24 13:46:44 -08:00

484 lines
10 KiB
C

// 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);
}
}
void
runtime·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();
}
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);
}