root/src/pkg/runtime/os_windows.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. getproccount
  2. runtime·osinit
  3. runtime·get_random_data
  4. runtime·goenvs
  5. runtime·exit
  6. runtime·write
  7. runtime·semasleep
  8. runtime·semawakeup
  9. runtime·semacreate
  10. runtime·newosproc
  11. runtime·mpreinit
  12. runtime·minit
  13. runtime·unminit
  14. runtime·nanotime
  15. time·now
  16. runtime·stdcall
  17. runtime·osyield
  18. runtime·usleep
  19. runtime·issigpanic
  20. runtime·sigpanic
  21. runtime·initsig
  22. runtime·ctrlhandler1
  23. profilem
  24. runtime·profileloop1
  25. runtime·resetcpuprofiler
  26. os·sigpipe
  27. runtime·memlimit
  28. runtime·crash

// 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_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "../../cmd/ld/textflag.h"

#pragma dynimport runtime·AddVectoredExceptionHandler AddVectoredExceptionHandler "kernel32.dll"
#pragma dynimport runtime·CloseHandle CloseHandle "kernel32.dll"
#pragma dynimport runtime·CreateEvent CreateEventA "kernel32.dll"
#pragma dynimport runtime·CreateThread CreateThread "kernel32.dll"
#pragma dynimport runtime·CreateWaitableTimer CreateWaitableTimerA "kernel32.dll"
#pragma dynimport runtime·CryptAcquireContextW CryptAcquireContextW "advapi32.dll"
#pragma dynimport runtime·CryptGenRandom CryptGenRandom "advapi32.dll"
#pragma dynimport runtime·CryptReleaseContext CryptReleaseContext "advapi32.dll"
#pragma dynimport runtime·DuplicateHandle DuplicateHandle "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·GetSystemInfo GetSystemInfo "kernel32.dll"
#pragma dynimport runtime·GetSystemTimeAsFileTime GetSystemTimeAsFileTime "kernel32.dll"
#pragma dynimport runtime·GetThreadContext GetThreadContext "kernel32.dll"
#pragma dynimport runtime·LoadLibrary LoadLibraryW "kernel32.dll"
#pragma dynimport runtime·LoadLibraryA LoadLibraryA "kernel32.dll"
#pragma dynimport runtime·NtWaitForSingleObject NtWaitForSingleObject "ntdll.dll"
#pragma dynimport runtime·ResumeThread ResumeThread "kernel32.dll"
#pragma dynimport runtime·SetConsoleCtrlHandler SetConsoleCtrlHandler "kernel32.dll"
#pragma dynimport runtime·SetEvent SetEvent "kernel32.dll"
#pragma dynimport runtime·SetProcessPriorityBoost SetProcessPriorityBoost "kernel32.dll"
#pragma dynimport runtime·SetThreadPriority SetThreadPriority "kernel32.dll"
#pragma dynimport runtime·SetWaitableTimer SetWaitableTimer "kernel32.dll"
#pragma dynimport runtime·Sleep Sleep "kernel32.dll"
#pragma dynimport runtime·SuspendThread SuspendThread "kernel32.dll"
#pragma dynimport runtime·WaitForSingleObject WaitForSingleObject "kernel32.dll"
#pragma dynimport runtime·WriteFile WriteFile "kernel32.dll"
#pragma dynimport runtime·timeBeginPeriod timeBeginPeriod "winmm.dll"

extern void *runtime·AddVectoredExceptionHandler;
extern void *runtime·CloseHandle;
extern void *runtime·CreateEvent;
extern void *runtime·CreateThread;
extern void *runtime·CreateWaitableTimer;
extern void *runtime·CryptAcquireContextW;
extern void *runtime·CryptGenRandom;
extern void *runtime·CryptReleaseContext;
extern void *runtime·DuplicateHandle;
extern void *runtime·ExitProcess;
extern void *runtime·FreeEnvironmentStringsW;
extern void *runtime·GetEnvironmentStringsW;
extern void *runtime·GetProcAddress;
extern void *runtime·GetStdHandle;
extern void *runtime·GetSystemInfo;
extern void *runtime·GetSystemTimeAsFileTime;
extern void *runtime·GetThreadContext;
extern void *runtime·LoadLibrary;
extern void *runtime·LoadLibraryA;
extern void *runtime·NtWaitForSingleObject;
extern void *runtime·ResumeThread;
extern void *runtime·SetConsoleCtrlHandler;
extern void *runtime·SetEvent;
extern void *runtime·SetProcessPriorityBoost;
extern void *runtime·SetThreadPriority;
extern void *runtime·SetWaitableTimer;
extern void *runtime·Sleep;
extern void *runtime·SuspendThread;
extern void *runtime·WaitForSingleObject;
extern void *runtime·WriteFile;
extern void *runtime·timeBeginPeriod;

void *runtime·GetQueuedCompletionStatusEx;

extern uintptr runtime·externalthreadhandlerp;
void runtime·externalthreadhandler(void);
void runtime·sigtramp(void);

static int32
getproccount(void)
{
        SystemInfo info;

        runtime·stdcall(runtime·GetSystemInfo, 1, &info);
        return info.dwNumberOfProcessors;
}

void
runtime·osinit(void)
{
        void *kernel32;

        runtime·externalthreadhandlerp = (uintptr)runtime·externalthreadhandler;

        runtime·stdcall(runtime·AddVectoredExceptionHandler, 2, (uintptr)1, (uintptr)runtime·sigtramp);
        runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1);
        runtime·stdcall(runtime·timeBeginPeriod, 1, (uintptr)1);
        runtime·ncpu = getproccount();
        
        // Windows dynamic priority boosting assumes that a process has different types
        // of dedicated threads -- GUI, IO, computational, etc. Go processes use
        // equivalent threads that all do a mix of GUI, IO, computations, etc.
        // In such context dynamic priority boosting does nothing but harm, so we turn it off.
        runtime·stdcall(runtime·SetProcessPriorityBoost, 2, (uintptr)-1, (uintptr)1);

        kernel32 = runtime·stdcall(runtime·LoadLibraryA, 1, "kernel32.dll");
        if(kernel32 != nil) {
                runtime·GetQueuedCompletionStatusEx = runtime·stdcall(runtime·GetProcAddress, 2, kernel32, "GetQueuedCompletionStatusEx");
        }
}

void
runtime·get_random_data(byte **rnd, int32 *rnd_len)
{
        uintptr handle;
        *rnd = nil;
        *rnd_len = 0;
        if(runtime·stdcall(runtime·CryptAcquireContextW, 5, &handle, nil, nil,
                           (uintptr)1 /* PROV_RSA_FULL */,
                           (uintptr)0xf0000000U /* CRYPT_VERIFYCONTEXT */) != 0) {
                static byte random_data[HashRandomBytes];
                if(runtime·stdcall(runtime·CryptGenRandom, 3, handle, (uintptr)HashRandomBytes, random_data)) {
                        *rnd = random_data;
                        *rnd_len = HashRandomBytes;
                }
                runtime·stdcall(runtime·CryptReleaseContext, 2, handle, (uintptr)0);
        }
}

void
runtime·goenvs(void)
{
        extern Slice syscall·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;
        }
        syscall·envs.array = (byte*)s;
        syscall·envs.len = n;
        syscall·envs.cap = n;

        runtime·stdcall(runtime·FreeEnvironmentStringsW, 1, env);
}

void
runtime·exit(int32 code)
{
        runtime·stdcall(runtime·ExitProcess, 1, (uintptr)code);
}

int32
runtime·write(uintptr 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:
                // assume fd is real windows handle.
                handle = (void*)fd;
                break;
        }
        runtime·stdcall(runtime·WriteFile, 5, handle, buf, (uintptr)n, &written, (uintptr)0);
        return written;
}

#define INFINITE ((uintptr)0xFFFFFFFF)

#pragma textflag NOSPLIT
int32
runtime·semasleep(int64 ns)
{
        // store ms in ns to save stack space
        if(ns < 0)
                ns = INFINITE;
        else {
                ns = runtime·timediv(ns, 1000000, nil);
                if(ns == 0)
                        ns = 1;
        }
        if(runtime·stdcall(runtime·WaitForSingleObject, 2, m->waitsema, (uintptr)ns) != 0)
                return -1;  // timeout
        return 0;
}

void
runtime·semawakeup(M *mp)
{
        runtime·stdcall(runtime·SetEvent, 1, mp->waitsema);
}

uintptr
runtime·semacreate(void)
{
        return (uintptr)runtime·stdcall(runtime·CreateEvent, 4, (uintptr)0, (uintptr)0, (uintptr)0, (uintptr)0);
}

#define STACK_SIZE_PARAM_IS_A_RESERVATION ((uintptr)0x00010000)

void
runtime·newosproc(M *mp, void *stk)
{
        void *thandle;

        USED(stk);

        thandle = runtime·stdcall(runtime·CreateThread, 6,
                nil, (uintptr)0x20000, runtime·tstart_stdcall, mp,
                STACK_SIZE_PARAM_IS_A_RESERVATION, nil);
        if(thandle == nil) {
                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).
// Called on the parent thread (main thread in case of bootstrap), can allocate memory.
void
runtime·mpreinit(M *mp)
{
        USED(mp);
}

// Called to initialize a new m (including the bootstrap m).
// Called on the new thread, can not allocate memory.
void
runtime·minit(void)
{
        void *thandle;

        // -1 = current process, -2 = current thread
        runtime·stdcall(runtime·DuplicateHandle, 7,
                (uintptr)-1, (uintptr)-2, (uintptr)-1, &thandle,
                (uintptr)0, (uintptr)0, (uintptr)DUPLICATE_SAME_ACCESS);
        runtime·atomicstorep(&m->thread, thandle);
}

// Called from dropm to undo the effect of an minit.
void
runtime·unminit(void)
{
}

#pragma textflag NOSPLIT
int64
runtime·nanotime(void)
{
        int64 filetime;

        runtime·stdcall(runtime·GetSystemTimeAsFileTime, 1, &filetime);

        // Filetime is 100s of nanoseconds since January 1, 1601.
        // Convert to nanoseconds since January 1, 1970.
        return (filetime - 116444736000000000LL) * 100LL;
}

void
time·now(int64 sec, int32 usec)
{
        int64 ns;

        ns = runtime·nanotime();
        sec = ns / 1000000000LL;
        usec = ns - sec * 1000000000LL;
        FLUSH(&sec);
        FLUSH(&usec);
}

// Calling stdcall on os stack.
#pragma textflag NOSPLIT
void *
runtime·stdcall(void *fn, int32 count, ...)
{
        m->libcall.fn = fn;
        m->libcall.n = count;
        m->libcall.args = (uintptr*)&count + 1;
        if(m->profilehz != 0) {
                // leave pc/sp for cpu profiler
                m->libcallg = g;
                m->libcallpc = (uintptr)runtime·getcallerpc(&fn);
                // sp must be the last, because once async cpu profiler finds
                // all three values to be non-zero, it will use them
                m->libcallsp = (uintptr)runtime·getcallersp(&fn);
        }
        runtime·asmcgocall(runtime·asmstdcall, &m->libcall);
        m->libcallsp = 0;
        return (void*)m->libcall.r1;
}

extern void runtime·usleep1(uint32);

#pragma textflag NOSPLIT
void
runtime·osyield(void)
{
        runtime·usleep1(1);
}

#pragma textflag NOSPLIT
void
runtime·usleep(uint32 us)
{
        // Have 1us units; want 100ns units.
        runtime·usleep1(10*us);
}

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)
{
        if(!runtime·canpanic(g))
                runtime·throw("unexpected signal during runtime execution");

        switch(g->sig) {
        case EXCEPTION_ACCESS_VIOLATION:
                if(g->sigcode1 < 0x1000 || g->paniconfault) {
                        if(g->sigpc == 0)
                                runtime·panicstring("call of nil func value");
                        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");
}

void
runtime·initsig(void)
{
        // following line keeps sigtramp alive at link stage
        // if there's a better way please write it here
        void *p = runtime·sigtramp;
        USED(p);
}

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;
}

extern void runtime·dosigprof(Context *r, G *gp, M *mp);
extern void runtime·profileloop(void);
static void *profiletimer;

static void
profilem(M *mp)
{
        extern M runtime·m0;
        extern uint32 runtime·tls0[];
        byte rbuf[sizeof(Context)+15];
        Context *r;
        void *tls;
        G *gp;

        tls = mp->tls;
        if(mp == &runtime·m0)
                tls = runtime·tls0;
        gp = *(G**)tls;

        // align Context to 16 bytes
        r = (Context*)((uintptr)(&rbuf[15]) & ~15);
        r->ContextFlags = CONTEXT_CONTROL;
        runtime·stdcall(runtime·GetThreadContext, 2, mp->thread, r);
        runtime·dosigprof(r, gp, mp);
}

void
runtime·profileloop1(void)
{
        M *mp, *allm;
        void *thread;

        runtime·stdcall(runtime·SetThreadPriority, 2,
                (uintptr)-2, (uintptr)THREAD_PRIORITY_HIGHEST);

        for(;;) {
                runtime·stdcall(runtime·WaitForSingleObject, 2, profiletimer, (uintptr)-1);
                allm = runtime·atomicloadp(&runtime·allm);
                for(mp = allm; mp != nil; mp = mp->alllink) {
                        thread = runtime·atomicloadp(&mp->thread);
                        // Do not profile threads blocked on Notes,
                        // this includes idle worker threads,
                        // idle timer thread, idle heap scavenger, etc.
                        if(thread == nil || mp->profilehz == 0 || mp->blocked)
                                continue;
                        runtime·stdcall(runtime·SuspendThread, 1, thread);
                        if(mp->profilehz != 0 && !mp->blocked)
                                profilem(mp);
                        runtime·stdcall(runtime·ResumeThread, 1, thread);
                }
        }
}

void
runtime·resetcpuprofiler(int32 hz)
{
        static Lock lock;
        void *timer, *thread;
        int32 ms;
        int64 due;

        runtime·lock(&lock);
        if(profiletimer == nil) {
                timer = runtime·stdcall(runtime·CreateWaitableTimer, 3, nil, nil, nil);
                runtime·atomicstorep(&profiletimer, timer);
                thread = runtime·stdcall(runtime·CreateThread, 6,
                        nil, nil, runtime·profileloop, nil, nil, nil);
                runtime·stdcall(runtime·CloseHandle, 1, thread);
        }
        runtime·unlock(&lock);

        ms = 0;
        due = 1LL<<63;
        if(hz > 0) {
                ms = 1000 / hz;
                if(ms == 0)
                        ms = 1;
                due = ms * -10000;
        }
        runtime·stdcall(runtime·SetWaitableTimer, 6,
                profiletimer, &due, (uintptr)ms, nil, nil, nil);
        runtime·atomicstore((uint32*)&m->profilehz, hz);
}

void
os·sigpipe(void)
{
        runtime·throw("too many writes on closed pipe");
}

uintptr
runtime·memlimit(void)
{
        return 0;
}

#pragma dataflag NOPTR
int8 runtime·badsignalmsg[] = "runtime: signal received on thread not created by Go.\n";
int32 runtime·badsignallen = sizeof runtime·badsignalmsg - 1;

void
runtime·crash(void)
{
        // TODO: This routine should do whatever is needed
        // to make the Windows program abort/crash as it
        // would if Go was not intercepting signals.
        // On Unix the routine would remove the custom signal
        // handler and then raise a signal (like SIGABRT).
        // Something like that should happen here.
        // It's okay to leave this empty for now: if crash returns
        // the ordinary exit-after-panic happens.
}

/* [<][>][^][v][top][bottom][index][help] */