corda/src/windows.cpp
Joel Dice c2a9424f91 implement compileTimeMemoryBarrier in arch.h
This has the same implementation as programOrderMemoryBarrier in
x86.h, but makes it available on all architectures.
2010-02-04 18:30:13 -07:00

924 lines
21 KiB
C++

/* Copyright (c) 2008-2009, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
#include "sys/stat.h"
#include "windows.h"
#include "sys/timeb.h"
#ifdef _MSC_VER
# define S_ISREG(x) ((x) & _S_IFREG)
# define S_ISDIR(x) ((x) & _S_IFDIR)
# define FTIME _ftime_s
#else
# define FTIME _ftime
#endif
#undef max
#undef min
#include "arch.h"
#include "system.h"
#define ACQUIRE(s, x) MutexResource MAKE_NAME(mutexResource_) (s, x)
using namespace vm;
namespace {
class MutexResource {
public:
MutexResource(System* s, HANDLE m): s(s), m(m) {
int r UNUSED = WaitForSingleObject(m, INFINITE);
assert(s, r == WAIT_OBJECT_0);
}
~MutexResource() {
bool success UNUSED = ReleaseMutex(m);
assert(s, success);
}
private:
System* s;
HANDLE m;
};
class MySystem;
MySystem* system;
LONG CALLBACK
handleException(LPEXCEPTION_POINTERS e);
DWORD WINAPI
run(void* r)
{
static_cast<System::Runnable*>(r)->run();
return 0;
}
const bool Verbose = false;
const unsigned Waiting = 1 << 0;
const unsigned Notified = 1 << 1;
class MySystem: public System {
public:
class Thread: public System::Thread {
public:
Thread(System* s, System::Runnable* r):
s(s),
r(r),
next(0),
flags(0)
{
mutex = CreateMutex(0, false, 0);
assert(s, mutex);
event = CreateEvent(0, true, false, 0);
assert(s, event);
}
virtual void interrupt() {
ACQUIRE(s, mutex);
r->setInterrupted(true);
if (flags & Waiting) {
int r UNUSED = SetEvent(event);
assert(s, r == 0);
}
}
virtual void join() {
int r UNUSED = WaitForSingleObject(thread, INFINITE);
assert(s, r == WAIT_OBJECT_0);
}
virtual void dispose() {
CloseHandle(event);
CloseHandle(mutex);
CloseHandle(thread);
s->free(this);
}
HANDLE thread;
HANDLE mutex;
HANDLE event;
System* s;
System::Runnable* r;
Thread* next;
unsigned flags;
};
class Mutex: public System::Mutex {
public:
Mutex(System* s): s(s) {
mutex = CreateMutex(0, false, 0);
assert(s, mutex);
}
virtual void acquire() {
int r UNUSED = WaitForSingleObject(mutex, INFINITE);
assert(s, r == WAIT_OBJECT_0);
}
virtual void release() {
bool success UNUSED = ReleaseMutex(mutex);
assert(s, success);
}
virtual void dispose() {
CloseHandle(mutex);
s->free(this);
}
System* s;
HANDLE mutex;
};
class Monitor: public System::Monitor {
public:
Monitor(System* s): s(s), owner_(0), first(0), last(0), depth(0) {
mutex = CreateMutex(0, false, 0);
assert(s, mutex);
}
virtual bool tryAcquire(System::Thread* context) {
Thread* t = static_cast<Thread*>(context);
assert(s, t);
if (owner_ == t) {
++ depth;
return true;
} else {
switch (WaitForSingleObject(mutex, 0)) {
case WAIT_TIMEOUT:
return false;
case WAIT_OBJECT_0:
owner_ = t;
++ depth;
return true;
default:
sysAbort(s);
}
}
}
virtual void acquire(System::Thread* context) {
Thread* t = static_cast<Thread*>(context);
assert(s, t);
if (owner_ != t) {
int r UNUSED = WaitForSingleObject(mutex, INFINITE);
assert(s, r == WAIT_OBJECT_0);
owner_ = t;
}
++ depth;
}
virtual void release(System::Thread* context) {
Thread* t = static_cast<Thread*>(context);
assert(s, t);
if (owner_ == t) {
if (-- depth == 0) {
owner_ = 0;
bool success UNUSED = ReleaseMutex(mutex);
assert(s, success);
}
} else {
sysAbort(s);
}
}
void append(Thread* t) {
if (last) {
last->next = t;
last = t;
} else {
first = last = t;
}
}
void remove(Thread* t) {
Thread* previous = 0;
for (Thread* current = first; current;) {
if (t == current) {
if (current == first) {
first = t->next;
} else {
previous->next = t->next;
}
if (current == last) {
last = previous;
}
t->next = 0;
break;
} else {
previous = current;
current = current->next;
}
}
}
virtual bool wait(System::Thread* context, int64_t time) {
Thread* t = static_cast<Thread*>(context);
assert(s, t);
if (owner_ == t) {
// Initialized here to make gcc 4.2 a happy compiler
bool interrupted = false;
bool notified = false;
unsigned depth = 0;
int r UNUSED;
{ ACQUIRE(s, t->mutex);
if (t->r->interrupted()) {
t->r->setInterrupted(false);
return true;
}
t->flags |= Waiting;
append(t);
depth = this->depth;
this->depth = 0;
owner_ = 0;
bool success UNUSED = ReleaseMutex(mutex);
assert(s, success);
success = ResetEvent(t->event);
assert(s, success);
success = ReleaseMutex(t->mutex);
assert(s, success);
r = WaitForSingleObject(t->event, (time ? time : INFINITE));
assert(s, r == WAIT_OBJECT_0 or r == WAIT_TIMEOUT);
r = WaitForSingleObject(t->mutex, INFINITE);
assert(s, r == WAIT_OBJECT_0);
notified = ((t->flags & Notified) != 0);
t->flags = 0;
interrupted = t->r->interrupted();
if (interrupted) {
t->r->setInterrupted(false);
}
}
r = WaitForSingleObject(mutex, INFINITE);
assert(s, r == WAIT_OBJECT_0);
if (not notified) {
remove(t);
}
t->next = 0;
owner_ = t;
this->depth = depth;
return interrupted;
} else {
sysAbort(s);
}
}
void doNotify(Thread* t) {
ACQUIRE(s, t->mutex);
t->flags |= Notified;
bool success UNUSED = SetEvent(t->event);
assert(s, success);
}
virtual void notify(System::Thread* context) {
Thread* t = static_cast<Thread*>(context);
assert(s, t);
if (owner_ == t) {
if (first) {
Thread* t = first;
first = first->next;
if (t == last) {
last = 0;
}
doNotify(t);
}
} else {
sysAbort(s);
}
}
virtual void notifyAll(System::Thread* context) {
Thread* t = static_cast<Thread*>(context);
assert(s, t);
if (owner_ == t) {
for (Thread* t = first; t; t = t->next) {
doNotify(t);
}
first = last = 0;
} else {
sysAbort(s);
}
}
virtual System::Thread* owner() {
return owner_;
}
virtual void dispose() {
assert(s, owner_ == 0);
CloseHandle(mutex);
s->free(this);
}
System* s;
HANDLE mutex;
Thread* owner_;
Thread* first;
Thread* last;
unsigned depth;
};
class Local: public System::Local {
public:
Local(System* s): s(s) {
key = TlsAlloc();
assert(s, key != TLS_OUT_OF_INDEXES);
}
virtual void* get() {
return TlsGetValue(key);
}
virtual void set(void* p) {
bool r UNUSED = TlsSetValue(key, p);
assert(s, r);
}
virtual void dispose() {
bool r UNUSED = TlsFree(key);
assert(s, r);
s->free(this);
}
System* s;
unsigned key;
};
class Region: public System::Region {
public:
Region(System* system, uint8_t* start, size_t length, HANDLE mapping,
HANDLE file):
system(system),
start_(start),
length_(length),
mapping(mapping),
file(file)
{ }
virtual const uint8_t* start() {
return start_;
}
virtual size_t length() {
return length_;
}
virtual void dispose() {
if (start_) {
if (start_) UnmapViewOfFile(start_);
if (mapping) CloseHandle(mapping);
if (file) CloseHandle(file);
}
system->free(this);
}
System* system;
uint8_t* start_;
size_t length_;
HANDLE mapping;
HANDLE file;
};
class Directory: public System::Directory {
public:
Directory(System* s): s(s), handle(0), findNext(false) { }
virtual const char* next() {
if (handle and handle != INVALID_HANDLE_VALUE) {
if (findNext) {
if (FindNextFile(handle, &data)) {
return data.cFileName;
}
} else {
findNext = true;
return data.cFileName;
}
}
return 0;
}
virtual void dispose() {
if (handle and handle != INVALID_HANDLE_VALUE) {
FindClose(handle);
}
s->free(this);
}
System* s;
HANDLE handle;
WIN32_FIND_DATA data;
bool findNext;
};
class Library: public System::Library {
public:
Library(System* s, HMODULE handle, const char* name, bool mapName):
s(s),
handle(handle),
name_(name),
mapName_(mapName),
next_(0)
{ }
virtual void* resolve(const char* function) {
void* address;
FARPROC p = GetProcAddress(handle, function);
memcpy(&address, &p, BytesPerWord);
return address;
}
virtual const char* name() {
return name_;
}
virtual bool mapName() {
return mapName_;
}
virtual System::Library* next() {
return next_;
}
virtual void setNext(System::Library* lib) {
next_ = lib;
}
virtual void disposeAll() {
if (Verbose) {
fprintf(stderr, "close %p\n", handle);
}
if (name_) {
FreeLibrary(handle);
}
if (next_) {
next_->disposeAll();
}
if (name_) {
s->free(name_);
}
s->free(this);
}
System* s;
HMODULE handle;
const char* name_;
bool mapName_;
System::Library* next_;
};
MySystem(const char* crashDumpDirectory):
segFaultHandler(0),
oldSegFaultHandler(0),
crashDumpDirectory(crashDumpDirectory)
{
expect(this, system == 0);
system = this;
mutex = CreateMutex(0, false, 0);
assert(this, mutex);
}
virtual void* tryAllocate(unsigned sizeInBytes) {
return malloc(sizeInBytes);
}
virtual void free(const void* p) {
if (p) ::free(const_cast<void*>(p));
}
virtual void* tryAllocateExecutable(unsigned sizeInBytes) {
return VirtualAlloc
(0, sizeInBytes, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
}
virtual void freeExecutable(const void* p, unsigned) {
int r UNUSED = VirtualFree(const_cast<void*>(p), 0, MEM_RELEASE);
assert(this, r);
}
virtual bool success(Status s) {
return s == 0;
}
virtual Status attach(Runnable* r) {
Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r);
bool success UNUSED = DuplicateHandle
(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
&(t->thread), 0, false, DUPLICATE_SAME_ACCESS);
assert(this, success);
r->attach(t);
return 0;
}
virtual Status start(Runnable* r) {
Thread* t = new (allocate(this, sizeof(Thread))) Thread(this, r);
r->attach(t);
DWORD id;
t->thread = CreateThread(0, 0, run, r, 0, &id);
assert(this, t->thread);
return 0;
}
virtual Status make(System::Mutex** m) {
*m = new (allocate(this, sizeof(Mutex))) Mutex(this);
return 0;
}
virtual Status make(System::Monitor** m) {
*m = new (allocate(this, sizeof(Monitor))) Monitor(this);
return 0;
}
virtual Status make(System::Local** l) {
*l = new (allocate(this, sizeof(Local))) Local(this);
return 0;
}
virtual Status handleSegFault(SignalHandler* handler) {
if (handler) {
segFaultHandler = handler;
#ifdef ARCH_x86_32
oldSegFaultHandler = SetUnhandledExceptionFilter(handleException);
#elif defined ARCH_x86_64
AddVectoredExceptionHandler(1, handleException);
oldSegFaultHandler = 0;
#endif
return 0;
} else if (segFaultHandler) {
segFaultHandler = 0;
#ifdef ARCH_x86_32
SetUnhandledExceptionFilter(oldSegFaultHandler);
#elif defined ARCH_x86_64
//do nothing, handlers are never "unregistered" anyway
#endif
return 0;
} else {
return 1;
}
}
virtual Status visit(System::Thread* st UNUSED, System::Thread* sTarget,
ThreadVisitor* visitor)
{
assert(this, st != sTarget);
Thread* target = static_cast<Thread*>(sTarget);
ACQUIRE(this, mutex);
bool success = false;
int rv = SuspendThread(target->thread);
if (rv != -1) {
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_CONTROL;
rv = GetThreadContext(target->thread, &context);
if (rv) {
#ifdef ARCH_x86_32
visitor->visit(reinterpret_cast<void*>(context.Eip),
reinterpret_cast<void*>(context.Ebp),
reinterpret_cast<void*>(context.Esp));
#elif defined ARCH_x86_64
visitor->visit(reinterpret_cast<void*>(context.Rip),
reinterpret_cast<void*>(context.Rbp),
reinterpret_cast<void*>(context.Rsp));
#endif
success = true;
}
rv = ResumeThread(target->thread);
expect(this, rv != -1);
}
return (success ? 0 : 1);
}
virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types,
unsigned count, unsigned size, unsigned returnType)
{
return dynamicCall(function, arguments, types, count, size, returnType);
}
virtual Status map(System::Region** region, const char* name) {
Status status = 1;
HANDLE file = CreateFile(name, FILE_READ_DATA, FILE_SHARE_READ, 0,
OPEN_EXISTING, 0, 0);
if (file != INVALID_HANDLE_VALUE) {
unsigned size = GetFileSize(file, 0);
if (size != INVALID_FILE_SIZE) {
HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, size, 0);
if (mapping) {
void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
if (data) {
*region = new (allocate(this, sizeof(Region)))
Region(this, static_cast<uint8_t*>(data), size, file, mapping);
status = 0;
}
if (status) {
CloseHandle(mapping);
}
}
}
if (status) {
CloseHandle(file);
}
}
return status;
}
virtual Status open(System::Directory** directory, const char* name) {
Status status = 1;
unsigned length = strlen(name);
RUNTIME_ARRAY(char, buffer, length + 3);
memcpy(RUNTIME_ARRAY_BODY(buffer), name, length);
memcpy(RUNTIME_ARRAY_BODY(buffer) + length, "\\*", 3);
Directory* d = new (allocate(this, sizeof(Directory))) Directory(this);
d->handle = FindFirstFile(RUNTIME_ARRAY_BODY(buffer), &(d->data));
if (d->handle == INVALID_HANDLE_VALUE) {
d->dispose();
} else {
*directory = d;
status = 0;
}
return status;
}
virtual FileType identify(const char* name) {
struct _stat s;
int r = _stat(name, &s);
if (r == 0) {
if (S_ISREG(s.st_mode)) {
return TypeFile;
} else if (S_ISDIR(s.st_mode)) {
return TypeDirectory;
} else {
return TypeUnknown;
}
} else {
return TypeDoesNotExist;
}
}
virtual Status load(System::Library** lib,
const char* name,
bool mapName)
{
HMODULE handle;
unsigned nameLength = (name ? strlen(name) : 0);
if (mapName and name) {
unsigned size = sizeof(SO_PREFIX) + nameLength + sizeof(SO_SUFFIX);
RUNTIME_ARRAY(char, buffer, size);;
vm::snprintf
(RUNTIME_ARRAY_BODY(buffer), size, SO_PREFIX "%s" SO_SUFFIX, name);
handle = LoadLibrary(RUNTIME_ARRAY_BODY(buffer));
} else if (name) {
handle = LoadLibrary(name);
} else {
handle = GetModuleHandle(0);
}
if (handle) {
if (Verbose) {
fprintf(stderr, "open %s as %p\n", name, handle);
}
char* n;
if (name) {
n = static_cast<char*>(allocate(this, nameLength + 1));
memcpy(n, name, nameLength + 1);
} else {
n = 0;
}
*lib = new (allocate(this, sizeof(Library)))
Library(this, handle, n, mapName);
return 0;
} else {
return 1;
}
}
virtual char pathSeparator() {
return ';';
}
virtual int64_t now() {
static LARGE_INTEGER frequency;
static LARGE_INTEGER time;
static bool init = true;
if (init) {
QueryPerformanceFrequency(&frequency);
if (frequency.QuadPart == 0) {
return 0;
}
init = false;
}
QueryPerformanceCounter(&time);
return static_cast<int64_t>
(((static_cast<double>(time.QuadPart)) * 1000.0) /
(static_cast<double>(frequency.QuadPart)));
}
virtual void exit(int code) {
::exit(code);
}
virtual void abort() {
// trigger an EXCEPTION_ACCESS_VIOLATION, which we will catch and
// generate a debug dump for
*static_cast<int*>(0) = 0;
}
virtual void dispose() {
system = 0;
CloseHandle(mutex);
::free(this);
}
HANDLE mutex;
System::SignalHandler* segFaultHandler;
LPTOP_LEVEL_EXCEPTION_FILTER oldSegFaultHandler;
const char* crashDumpDirectory;
};
struct MINIDUMP_EXCEPTION_INFORMATION {
DWORD thread;
LPEXCEPTION_POINTERS exception;
BOOL exceptionInCurrentAddressSpace;
};
struct MINIDUMP_USER_STREAM_INFORMATION;
struct MINIDUMP_CALLBACK_INFORMATION;
enum MINIDUMP_TYPE {
MiniDumpNormal = 0,
MiniDumpWithFullMemory = 2
};
typedef BOOL (*MiniDumpWriteDumpType)
(HANDLE processHandle,
DWORD processId,
HANDLE file,
MINIDUMP_TYPE type,
const MINIDUMP_EXCEPTION_INFORMATION* exception,
const MINIDUMP_USER_STREAM_INFORMATION* userStream,
const MINIDUMP_CALLBACK_INFORMATION* callback);
void
dump(LPEXCEPTION_POINTERS e, const char* directory)
{
HINSTANCE dbghelp = LoadLibrary("dbghelp.dll");
if (dbghelp) {
MiniDumpWriteDumpType MiniDumpWriteDump = reinterpret_cast
<MiniDumpWriteDumpType>(GetProcAddress(dbghelp, "MiniDumpWriteDump"));
if (MiniDumpWriteDump) {
char name[MAX_PATH];
_timeb tb;
FTIME(&tb);
vm::snprintf(name, MAX_PATH, "%s\\crash-%"LLD".mdmp", directory,
(static_cast<int64_t>(tb.time) * 1000)
+ static_cast<int64_t>(tb.millitm));
HANDLE file = CreateFile
(name, FILE_WRITE_DATA, 0, 0, CREATE_ALWAYS, 0, 0);
if (file != INVALID_HANDLE_VALUE) {
MINIDUMP_EXCEPTION_INFORMATION exception
= { GetCurrentThreadId(), e, true };
MiniDumpWriteDump
(GetCurrentProcess(),
GetCurrentProcessId(),
file,
MiniDumpWithFullMemory,
&exception,
0,
0);
CloseHandle(file);
}
}
FreeLibrary(dbghelp);
}
}
LONG CALLBACK
handleException(LPEXCEPTION_POINTERS e)
{
if (e->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
#ifdef ARCH_x86_32
void* ip = reinterpret_cast<void*>(e->ContextRecord->Eip);
void* base = reinterpret_cast<void*>(e->ContextRecord->Ebp);
void* stack = reinterpret_cast<void*>(e->ContextRecord->Esp);
void* thread = reinterpret_cast<void*>(e->ContextRecord->Ebx);
#elif defined ARCH_x86_64
void* ip = reinterpret_cast<void*>(e->ContextRecord->Rip);
void* base = reinterpret_cast<void*>(e->ContextRecord->Rbp);
void* stack = reinterpret_cast<void*>(e->ContextRecord->Rsp);
void* thread = reinterpret_cast<void*>(e->ContextRecord->Rbx);
#endif
bool jump = system->segFaultHandler->handleSignal
(&ip, &base, &stack, &thread);
#ifdef ARCH_x86_32
e->ContextRecord->Eip = reinterpret_cast<DWORD>(ip);
e->ContextRecord->Ebp = reinterpret_cast<DWORD>(base);
e->ContextRecord->Esp = reinterpret_cast<DWORD>(stack);
e->ContextRecord->Ebx = reinterpret_cast<DWORD>(thread);
#elif defined ARCH_x86_64
e->ContextRecord->Rip = reinterpret_cast<DWORD64>(ip);
e->ContextRecord->Rbp = reinterpret_cast<DWORD64>(base);
e->ContextRecord->Rsp = reinterpret_cast<DWORD64>(stack);
e->ContextRecord->Rbx = reinterpret_cast<DWORD64>(thread);
#endif
if (jump) {
return EXCEPTION_CONTINUE_EXECUTION;
}
if (system->crashDumpDirectory) {
dump(e, system->crashDumpDirectory);
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
} // namespace
namespace vm {
System*
makeSystem(const char* crashDumpDirectory)
{
return new (malloc(sizeof(MySystem))) MySystem(crashDumpDirectory);
}
} // namespace vm