t;
}

static CRITICAL_SECTION* system_state(void)
{
    static int initialized = 0;
    static CRITICAL_SECTION syssect;

    if (!initialized) {
	InitializeCriticalSection(&syssect);
	initialized = 1;
    }
    return &syssect;
}

static LONG flag_interrupt = -1;
static volatile DWORD tlsi_interrupt = TLS_OUT_OF_INDEXES;

void win32_disable_interrupt(void)
{
    if (IsWinNT()) {
	EnterCriticalSection(system_state());
	return;
    }

    if (tlsi_interrupt == TLS_OUT_OF_INDEXES) {
	tlsi_interrupt = TlsAlloc();
    }

    {
	DWORD ti = (DWORD)TlsGetValue(tlsi_interrupt);
	while (InterlockedIncrement(&flag_interrupt) > 0 && !ti) {
	    InterlockedDecrement(&flag_interrupt);
	    Sleep(1);
	}
	TlsSetValue(tlsi_interrupt, (PVOID)++ti);
    }
}

void win32_enable_interrupt(void)
{
    if (IsWinNT()) {
	LeaveCriticalSection(system_state());
	return;
    }

    InterlockedDecrement(&flag_interrupt);
    TlsSetValue(tlsi_interrupt, (PVOID)((DWORD)TlsGetValue(tlsi_interrupt) - 1));
}

struct handler_arg_t {
    void (*handler)(int);
    int arg;
    int status;
    int userstate;
    HANDLE handshake;
};

static void win32_call_handler(struct handler_arg_t* h)
{
    int status;
    RUBY_CRITICAL(rb_protect((VALUE (*)())h->handler, (VALUE)h->arg, &h->status);
		  status = h->status;
		  SetEvent(h->handshake));
    if (status) {
	rb_jump_tag(status);
    }
    h->userstate = 1;		/* never syscall after here */
    for (;;);			/* wait here in user state */
}

static struct handler_arg_t* setup_handler(struct handler_arg_t *harg,
					   int arg,
					   void (*handler)(int),
					   HANDLE handshake)
{
    harg->handler = handler;
    harg->arg = arg;
    harg->status = 0;
    harg->userstate = 0;
    harg->handshake = handshake;
    return harg;
}

static void setup_call(CONTEXT* ctx, struct handler_arg_t *harg)
{
#ifdef _M_IX86
    DWORD *esp = (DWORD *)ctx->Esp;
    *--esp = (DWORD)harg;
    *--esp = ctx->Eip;
    ctx->Esp = (DWORD)esp;
    ctx->Eip = (DWORD)win32_call_handler;
#else
#error unsupported processor
#endif
}

int win32_main_context(int arg, void (*handler)(int))
{
    static HANDLE interrupt_done = NULL;
    struct handler_arg_t harg;
    CONTEXT ctx_orig;
    HANDLE current_thread = GetCurrentThread();
    int old_priority = GetThreadPriority(current_thread);

    if (GetCurrentThreadId() == main_thread.id) return FALSE;

    SetSignal(interrupted_event);

    RUBY_CRITICAL({		/* the main thread must be in user state */
	CONTEXT ctx;

	SuspendThread(main_thread.handle);
	SetThreadPriority(current_thread, GetThreadPriority(main_thread.handle));

	ZeroMemory(&ctx, sizeof(CONTEXT));
	ctx.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
	GetThreadContext(main_thread.handle, &ctx);
	ctx_orig = ctx;

	/* handler context setup */
	if (!interrupt_done) {
	    interrupt_done = CreateEvent(NULL, FALSE, FALSE, NULL);
	    /* anonymous one-shot event */
	}
	else {
	    ResetEvent(interrupt_done);
	}
	setup_call(&ctx, setup_handler(&harg, arg, handler, interrupt_done));

	ctx.ContextFlags = CONTEXT_CONTROL;
	SetThreadContext(main_thread.handle, &ctx);
	ResumeThread(main_thread.handle);
    });

    /* give a chance to the main thread */
    yield_once();
    WaitForSingleObject(interrupt_done, INFINITE); /* handshaking */

    if (!harg.status) {
	/* no exceptions raised, restore old context. */
	RUBY_CRITICAL({
	    /* ensure the main thread is in user state. */
	    yield_until(harg.userstate);

	    SuspendThread(main_thread.handle);
	    ctx_orig.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
	    SetThreadContext(main_thread.handle, &ctx_orig);
	    ResumeThread(main_thread.handle);
	});
    }
    /* otherwise leave the main thread raised */

    SetThreadPriority(current_thread, old_priority);

    return TRUE;
}

int win32_sleep(unsigned long msec)
{
    return wait_events(NULL, msec) != WAIT_TIMEOUT;
}

static void catch_interrupt(void)
{
    yield_once();
    win32_sleep(0);
    CHECK_INTS;
}

void win32_enter_syscall(void)
{
    InterlockedExchange(&rb_trap_immediate, 1);
    catch_interrupt();
    win32_disable_interrupt();
}

void win32_leave_syscall(void)
{
    win32_enable_interrupt();
    catch_interrupt();
    InterlockedExchange(&rb_trap_immediate, 0);
}

struct asynchronous_arg_t {
    /* output field */
    void* stackaddr;
    int errnum;

    /* input field */
    VALUE (*func)(VALUE self, int argc, VALUE* argv);
    VALUE self;
    int argc;
    VALUE* argv;
};

static DWORD WINAPI
call_asynchronous(PVOID argp)
{
    DWORD ret;
    struct asynchronous_arg_t *arg = argp;
    arg->stackaddr = &argp;
    ret = (DWORD)arg->func(arg->self, arg->argc, arg->argv);
    arg->errnum = errno;
    return ret;
}

VALUE
win32_asynchronize(asynchronous_func_t func,
		   VALUE self, int argc, VALUE* argv, VALUE intrval)
{
    DWORD val;
    BOOL interrupted = FALSE;
    HANDLE thr;

    RUBY_CRITICAL({
	struct asynchronous_arg_t arg;

	arg.stackaddr = NULL;
	arg.errnum = 0;
	arg.func = func;
	arg.self = self;
	arg.argc = argc;
	arg.argv = argv;

	thr = CreateThread(NULL, 0, call_asynchronous, &arg, 0, &val);

	if (thr) {
	    yield_until(arg.stackaddr);

	    if (wait_events(thr, INFINITE) != WAIT_OBJECT_0) {
		interrupted = TRUE;

		if (TerminateThread(thr, intrval)) {
		    yield_once();
		}
	    }

	    GetExitCodeThread(thr, &val);
	    CloseHandle(thr);

	    if (interrupted) {
		/* must release stack of killed thread, why doesn't Windows? */
		MEMORY_BASIC_INFORMATION m;

		memset(&m, 0, sizeof(m));
		if (!VirtualQuery(arg.stackaddr, &m, sizeof(m))) {
		    Debug(fprintf(stderr, "couldn't get stack base:%p:%d\n",
				  arg.stackaddr, GetLastError()));
		}
		else if (!VirtualFree(m.AllocationBase, 0, MEM_RELEASE)) {
		    Debug(fprintf(stderr, "couldn't release stack:%p:%d\n",
				  m.AllocationBase, GetLastError()));
		}
		errno = EINTR;
	    }
	    else {
		errno = arg.errnum;
	    }
	}
    });

    if (!thr) {
	rb_fatal("failed to launch waiter thread:%d", GetLastError());
    }

    if (interrupted) {
	CHECK_INTS;
    }

    return val;
}

char **win32_get_environ(void)
{
    char *envtop, *env;
    char **myenvtop, **myenv;
    int num;

    envtop = GetEnvironmentStrings();
    for (env = envtop, num = 0; *env; env += strlen(env) + 1)
	if (*env != '=') num++;

    myenvtop = ALLOC_N(char*, num + 1);
    for (env = envtop, myenv = myenvtop; *env; env += strlen(env) + 1) {
	if (*env != '=') {
	    *myenv = ALLOC_N(char, strlen(env) + 1);
	    strcpy(*myenv, env);
	    myenv++;
	}
    }
    *myenv = NULL;
    FreeEnvironmentStrings(envtop);

    return myenvtop;
}

void win32_free_environ(char **env)
{
    char **t = env;

    while (*t) free(*t++);
    free(env);
}

int
win32_fclose(FILE *fp)
{
    int fd = fileno(fp);
    SOCKET sock = TO_SOCKET(fd);

    if (fflush(fp)) return -1;
    if (!is_socket(sock)) {
	UnlockFile((HANDLE)sock, 0, 0, LK_LEN, 0);
	return fclose(fp);
    }
    _set_osfhnd(fd, (SOCKET)INVALID_HANDLE_VALUE);
    fclose(fp);
    if (closesocket(sock) == SOCKET_ERROR) {
	errno = WSAGetLastError();
	return -1;
    }
    return 0;
}

int
win32_close(int fd)
{
    SOCKET sock = TO_SOCKET(fd);

    if (!is_socket(sock)) {
	UnlockFile((HANDLE)sock, 0, 0, LK_LEN, 0);
	return _close(fd);
    }
    if (closesocket(sock) == SOCKET_ERROR) {
	errno = WSAGetLastError();
	return -1;
    }
    return 0;
}
