diff options
Diffstat (limited to 'src/pkg/runtime/windows/thread.c')
-rw-r--r-- | src/pkg/runtime/windows/thread.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c new file mode 100644 index 000000000..e08d1b6f0 --- /dev/null +++ b/src/pkg/runtime/windows/thread.c @@ -0,0 +1,432 @@ +// 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 "type.h" +#include "defs.h" +#include "os.h" + +#pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll" +#pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll" +#pragma dynimport runtime·CreateThread CreateThread "kernel32.dll" +#pragma dynimport runtime·ExitProcess ExitProcess "kernel32.dll" +#pragma dynimport runtime·FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll" +#pragma dynimport runtime·GetEnvironmentStringsW GetEnvironmentStringsW "kernel32.dll" +#pragma dynimport runtime·GetProcAddress GetProcAddress "kernel32.dll" +#pragma dynimport runtime·GetStdHandle GetStdHandle "kernel32.dll" +#pragma dynimport runtime·LoadLibraryEx LoadLibraryExA "kernel32.dll" +#pragma dynimport runtime·QueryPerformanceCounter QueryPerformanceCounter "kernel32.dll" +#pragma dynimport runtime·QueryPerformanceFrequency QueryPerformanceFrequency "kernel32.dll" +#pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll" +#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll" +#pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll" +#pragma dynimport runtime·WriteFile WriteFile "kernel32.dll" + +extern void *runtime·CloseHandle; +extern void *runtime·CreateEvent; +extern void *runtime·CreateThread; +extern void *runtime·ExitProcess; +extern void *runtime·FreeEnvironmentStringsW; +extern void *runtime·GetEnvironmentStringsW; +extern void *runtime·GetProcAddress; +extern void *runtime·GetStdHandle; +extern void *runtime·LoadLibraryEx; +extern void *runtime·QueryPerformanceCounter; +extern void *runtime·QueryPerformanceFrequency; +extern void *runtime·SetConsoleCtrlHandler; +extern void *runtime·SetEvent; +extern void *runtime·WaitForSingleObject; +extern void *runtime·WriteFile; + +static int64 timerfreq; +static void destroylock(Lock *l); + +void +runtime·osinit(void) +{ + runtime·stdcall(runtime·QueryPerformanceFrequency, 1, &timerfreq); + runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1); + runtime·destroylock = destroylock; +} + +void +runtime·goenvs(void) +{ + extern Slice os·Envs; + + uint16 *env; + String *s; + int32 i, n; + uint16 *p; + + env = runtime·stdcall(runtime·GetEnvironmentStringsW, 0); + + n = 0; + for(p=env; *p; n++) + p += runtime·findnullw(p)+1; + + s = runtime·malloc(n*sizeof s[0]); + + p = env; + for(i=0; i<n; i++) { + s[i] = runtime·gostringw(p); + p += runtime·findnullw(p)+1; + } + os·Envs.array = (byte*)s; + os·Envs.len = n; + os·Envs.cap = n; + + runtime·stdcall(runtime·FreeEnvironmentStringsW, 1, env); +} + +void +runtime·exit(int32 code) +{ + runtime·stdcall(runtime·ExitProcess, 1, (uintptr)code); +} + +int32 +runtime·write(int32 fd, void *buf, int32 n) +{ + void *handle; + uint32 written; + + written = 0; + switch(fd) { + case 1: + handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-11); + break; + case 2: + handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-12); + break; + default: + return -1; + } + runtime·stdcall(runtime·WriteFile, 5, handle, buf, (uintptr)n, &written, (uintptr)0); + return written; +} + +// Thread-safe allocation of an event. +static void +initevent(void **pevent) +{ + void *event; + + event = runtime·stdcall(runtime·CreateEvent, 4, (uintptr)0, (uintptr)0, (uintptr)0, (uintptr)0); + if(!runtime·casp(pevent, 0, event)) { + // Someone else filled it in. Use theirs. + runtime·stdcall(runtime·CloseHandle, 1, event); + } +} + +static void +eventlock(Lock *l) +{ + // Allocate event if needed. + if(l->event == 0) + initevent(&l->event); + + if(runtime·xadd(&l->key, 1) > 1) // someone else has it; wait + runtime·stdcall(runtime·WaitForSingleObject, 2, l->event, (uintptr)-1); +} + +static void +eventunlock(Lock *l) +{ + if(runtime·xadd(&l->key, -1) > 0) // someone else is waiting + runtime·stdcall(runtime·SetEvent, 1, l->event); +} + +void +runtime·lock(Lock *l) +{ + if(m->locks < 0) + runtime·throw("lock count"); + m->locks++; + eventlock(l); +} + +void +runtime·unlock(Lock *l) +{ + m->locks--; + if(m->locks < 0) + runtime·throw("lock count"); + eventunlock(l); +} + +static void +destroylock(Lock *l) +{ + if(l->event != 0) + runtime·stdcall(runtime·CloseHandle, 1, l->event); +} + +void +runtime·noteclear(Note *n) +{ + n->lock.key = 0; // memset(n, 0, sizeof *n) + eventlock(&n->lock); +} + +void +runtime·notewakeup(Note *n) +{ + eventunlock(&n->lock); +} + +void +runtime·notesleep(Note *n) +{ + eventlock(&n->lock); + eventunlock(&n->lock); // Let other sleepers find out too. +} + +void +runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) +{ + void *thandle; + + USED(stk); + USED(g); // assuming g = m->g0 + USED(fn); // assuming fn = mstart + + thandle = runtime·stdcall(runtime·CreateThread, 6, (uintptr)0, (uintptr)0, runtime·tstart_stdcall, m, (uintptr)0, (uintptr)0); + if(thandle == 0) { + runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror()); + runtime·throw("runtime.newosproc"); + } +} + +// Called to initialize a new m (including the bootstrap m). +void +runtime·minit(void) +{ +} + +void +runtime·gettime(int64 *sec, int32 *usec) +{ + int64 count; + + runtime·stdcall(runtime·QueryPerformanceCounter, 1, &count); + *sec = count / timerfreq; + count %= timerfreq; + *usec = count*1000000 / timerfreq; +} + +// Calling stdcall on os stack. +#pragma textflag 7 +void * +runtime·stdcall(void *fn, int32 count, ...) +{ + return runtime·stdcall_raw(fn, count, (uintptr*)&count + 1); +} + +uintptr +runtime·syscall(void *fn, uintptr nargs, void *args, uintptr *err) +{ + G *oldlock; + uintptr ret; + + /* + * Lock g to m to ensure we stay on the same stack if we do a callback. + */ + oldlock = m->lockedg; + m->lockedg = g; + g->lockedm = m; + + runtime·entersyscall(); + runtime·setlasterror(0); + ret = (uintptr)runtime·stdcall_raw(fn, nargs, args); + if(err) + *err = runtime·getlasterror(); + runtime·exitsyscall(); + + m->lockedg = oldlock; + if(oldlock == nil) + g->lockedm = nil; + + return ret; +} + +uint32 +runtime·issigpanic(uint32 code) +{ + switch(code) { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_UNDERFLOW: + return 1; + } + return 0; +} + +void +runtime·sigpanic(void) +{ + switch(g->sig) { + case EXCEPTION_ACCESS_VIOLATION: + if(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 EXCEPTION_INT_DIVIDE_BY_ZERO: + runtime·panicstring("integer divide by zero"); + case EXCEPTION_INT_OVERFLOW: + runtime·panicstring("integer overflow"); + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_UNDERFLOW: + runtime·panicstring("floating point error"); + } + runtime·throw("fault"); +} + +String +runtime·signame(int32 sig) +{ + int8 *s; + + switch(sig) { + case SIGINT: + s = "SIGINT: interrupt"; + break; + default: + return runtime·emptystring; + } + return runtime·gostringnocopy((byte*)s); +} + +uint32 +runtime·ctrlhandler1(uint32 type) +{ + int32 s; + + switch(type) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + s = SIGINT; + break; + default: + return 0; + } + + if(runtime·sigsend(s)) + return 1; + runtime·exit(2); // SIGINT, SIGTERM, etc + return 0; +} + +// Will keep all callbacks in a linked list, so they don't get garbage collected. +typedef struct Callback Callback; +struct Callback { + Callback* link; + void* gobody; + byte asmbody; +}; + +typedef struct Callbacks Callbacks; +struct Callbacks { + Lock; + Callback* link; + int32 n; +}; + +static Callbacks cbs; + +// Call back from windows dll into go. +byte * +runtime·compilecallback(Eface fn, bool cleanstack) +{ + FuncType *ft; + Type *t; + int32 argsize, i, n; + byte *p; + Callback *c; + + if(fn.type == nil || fn.type->kind != KindFunc) + runtime·panicstring("compilecallback: not a function"); + ft = (FuncType*)fn.type; + if(ft->out.len != 1) + runtime·panicstring("compilecallback: function must have one output parameter"); + if(((Type**)ft->out.array)[0]->size != sizeof(uintptr)) + runtime·panicstring("compilecallback: output parameter size is wrong"); + argsize = 0; + for(i=0; i<ft->in.len; i++) { + t = ((Type**)ft->in.array)[i]; + if(t->size != sizeof(uintptr)) + runtime·panicstring("compilecallback: input parameter size is wrong"); + argsize += t->size; + } + + // compute size of new fn. + // must match code laid out below. + n = 1+4; // MOVL fn, AX + n += 1+4; // MOVL argsize, DX + n += 1+4; // MOVL callbackasm, CX + n += 2; // CALL CX + n += 1; // RET + if(cleanstack) + n += 2; // ... argsize + + runtime·lock(&cbs); + for(c = cbs.link; c != nil; c = c->link) { + if(c->gobody == fn.data) { + runtime·unlock(&cbs); + return &c->asmbody; + } + } + if(cbs.n >= 2000) + runtime·throw("too many callback functions"); + c = runtime·mal(sizeof *c + n); + c->gobody = fn.data; + c->link = cbs.link; + cbs.link = c; + cbs.n++; + runtime·unlock(&cbs); + + p = &c->asmbody; + + // MOVL fn, AX + *p++ = 0xb8; + *(uint32*)p = (uint32)fn.data; + p += 4; + + // MOVL argsize, DX + *p++ = 0xba; + *(uint32*)p = argsize; + p += 4; + + // MOVL callbackasm, CX + *p++ = 0xb9; + *(uint32*)p = (uint32)runtime·callbackasm; + p += 4; + + // CALL CX + *p++ = 0xff; + *p++ = 0xd1; + + // RET argsize? + if(cleanstack) { + *p++ = 0xc2; + *(uint16*)p = argsize; + } else + *p = 0xc3; + + return &c->asmbody; +} + +void +os·sigpipe(void) +{ + runtime·throw("too many writes on closed pipe"); +} |