// Written in the D programming language

/*
 *  Copyright (C) 2002-2007 by Digital Mars, www.digitalmars.com
 *  Written by Walter Bright
 *
 *  This software is provided 'as-is', without any express or implied
 *  warranty. In no event will the authors be held liable for any damages
 *  arising from the use of this software.
 *
 *  Permission is granted to anyone to use this software for any purpose,
 *  including commercial applications, and to alter it and redistribute it
 *  freely, subject to the following restrictions:
 *
 *  o  The origin of this software must not be misrepresented; you must not
 *     claim that you wrote the original software. If you use this software
 *     in a product, an acknowledgment in the product documentation would be
 *     appreciated but is not required.
 *  o  Altered source versions must be plainly marked as such, and must not
 *     be misrepresented as being the original software.
 *  o  This notice may not be removed or altered from any source
 *     distribution.
 */

/**************************
 * The thread module defines the class $(B Thread).
 *
 * $(B Thread) is the basis
 * for writing multithreaded applications. Each thread
 * has a unique instance of class $(B Thread) associated with it.
 * It is important to use the $(B Thread) class to create and manage
 * threads as the garbage collector needs to know about all the threads.
 * Macros:
 *	WIKI=Phobos/StdThread
 */

module std.thread;

import std.c.stdio;

//debug=thread;

/* ================================ Win32 ================================= */

version (Win32)
{

private import std.c.windows.windows;

extern (Windows) alias uint (*stdfp)(void *);

extern (C)
    thread_hdl _beginthreadex(void* security, uint stack_size,
	stdfp start_addr, void* arglist, uint initflag,
	thread_id* thrdaddr);

/**
 * The type of the thread handle used by the operating system.
 * For Windows, it is equivalent to a HANDLE from windows.d.
 */
alias HANDLE thread_hdl;

alias uint thread_id;

/**
 * Thrown for errors.
 */
class ThreadError : Error
{
    this(string s)
    {
	super("Thread error: " ~ s);
    }
}

/**
 * One of these is created for each thread.
 */
class Thread
{
    /**
     * Constructor used by classes derived from Thread that override main(). 
     * The optional stacksize parameter default value of 0 will cause threads
     * to be created with the default size for the executable - Dave Fladebo
     */
    this(size_t stacksize = 0)
    {
	this.stacksize = stacksize;
    }

    /**
     * Constructor used by classes derived from Thread that override run().
     */
    this(int (*fp)(void *), void *arg, size_t stacksize = 0)
    {
	this.fp = fp;
	this.arg = arg;
	this.stacksize = stacksize;
    }

    /**
     * Constructor used by classes derived from Thread that override run().
     */
    this(int delegate() dg, size_t stacksize = 0)
    {
	this.dg = dg;
	this.stacksize = stacksize;
    }

    /**
     * Destructor
     *
     * If the thread hasn't been joined yet, detach it.
     */
    ~this()
    {
        if (state != TS.FINISHED)
            CloseHandle(hdl);
    }

    /**
     * The handle to this thread assigned by the operating system. This is set
     * to thread_id.init if the thread hasn't been started yet.
     */
    thread_hdl hdl;

    void* stackBottom;

    /**
     * Create a new thread and start it running. The new thread initializes
     * itself and then calls run(). start() can only be called once.
     */
    void start()
    {
	if (state != TS.INITIAL)
	    error("already started");

	synchronized (Thread.classinfo)
	{
	    for (int i = 0; 1; i++)
	    {
		if (i == allThreads.length)
		    error("too many threads");
		if (!allThreads[i])
		{   allThreads[i] = this;
		    idx = i;
		    if (i >= allThreadsDim)
			allThreadsDim = i + 1;
		    break;
		}
	    }
	    nthreads++;
	}

	state = TS.RUNNING;
	hdl = _beginthreadex(null, cast(uint)stacksize, &threadstart, cast(void*)this, 0, &id);
	if (hdl == cast(thread_hdl)0)
	{   state = TS.FINISHED;
	    synchronized (Thread.classinfo) allThreads[idx] = null;
	    idx = -1;
	    error("failed to start");
	}
    }

    /**
     * Entry point for a thread. If not overridden, it calls the function
     * pointer fp and argument arg passed in the constructor, or the delegate
     * dg.
     * Returns: the thread exit code, which is normally 0.
     */
    int run()
    {
	if (fp)
	    return fp(arg);
	else if (dg)
	    return dg();
	assert(0);
    }

    /*****************************
     * Wait for this thread to terminate.
     * Simply returns if thread has already terminated.
     * Throws: $(B ThreadError) if the thread hasn't begun yet or
     * is called on itself.
     */
    void wait()
    {
	if (isSelf)
	    error("wait on self");
	if (state != TS.FINISHED)
	{   DWORD dw;

	    dw = WaitForSingleObject(hdl, 0xFFFFFFFF);
            state = TS.FINISHED;
            CloseHandle(hdl);
            hdl = null;
	}
    }

    /******************************
     * Wait for this thread to terminate or until milliseconds time has
     * elapsed, whichever occurs first.
     * Simply returns if thread has already terminated.
     * Throws: $(B ThreadError) if the thread hasn't begun yet or
     * is called on itself.
     */
    void wait(uint milliseconds)
    {
	if (isSelf)
	    error("wait on self");
	if (state != TS.FINISHED)
	{   DWORD dw;

	    dw = WaitForSingleObject(hdl, milliseconds);
            state = TS.FINISHED;
            CloseHandle(hdl);
            hdl = null;
	}
    }

    /**
     * The state of a thread.
     */
    enum TS
    {
	INITIAL,	/// The thread hasn't been started yet.
	RUNNING,	/// The thread is running or paused.
	TERMINATED,	/// The thread has ended.
        FINISHED        /// The thread has been cleaned up
    }

    /**
     * Returns the state of a thread.
     */
    TS getState()
    {
	return state;
    }

    /**
     * The priority of a thread.
     */
    enum PRIORITY
    {
	INCREASE,	/// Increase thread priority
	DECREASE,	/// Decrease thread priority
	IDLE,		/// Assign thread low priority
	CRITICAL	/// Assign thread high priority
    }

    /**
     * Adjust the priority of this thread.
     * Throws: ThreadError if cannot set priority
     */
    void setPriority(PRIORITY p)
    {
	int nPriority;

	switch (p)
	{
	    case PRIORITY.INCREASE:
		nPriority = THREAD_PRIORITY_ABOVE_NORMAL;
		break;
	    case PRIORITY.DECREASE:
		nPriority = THREAD_PRIORITY_BELOW_NORMAL;
		break;
	    case PRIORITY.IDLE:
		nPriority = THREAD_PRIORITY_IDLE;
		break;
	    case PRIORITY.CRITICAL:
		nPriority = THREAD_PRIORITY_TIME_CRITICAL;
		break;
	    default:
		assert(0);
	}

	if (SetThreadPriority(hdl, nPriority) == THREAD_PRIORITY_ERROR_RETURN)
	    error("set priority");
    }

    /**
     * Returns true if this thread is the current thread.
     */
    bool isSelf()
    {
	//printf("id = %d, self = %d\n", id, pthread_self());
	return (id == GetCurrentThreadId());
    }

    /**
     * Returns a reference to the Thread for the thread that called the
     * function.
     */
    static Thread getThis()
    {
	//printf("getThis(), allThreadsDim = %d\n", allThreadsDim);
        thread_id id = GetCurrentThreadId();
        for (int i = 0; i < allThreadsDim; i++)
        {
            Thread t = allThreads[i];
            if (t && id == t.id)
            {
                return t;
            }
        }
	printf("didn't find it\n");
	assert(0);
    }

    /**
     * Returns an array of all the threads currently running.
     */
    static Thread[] getAll()
    {
	synchronized (Thread.classinfo) return allThreads[0 .. allThreadsDim];
    }

    /**
     * Suspend execution of this thread.
     */
    void pause()
    {
	if (state != TS.RUNNING || SuspendThread(hdl) == 0xFFFFFFFF)
	    error("cannot pause");
    }

    /**
     * Resume execution of this thread.
     */
    void resume()
    {
	if (state != TS.RUNNING || ResumeThread(hdl) == 0xFFFFFFFF)
	    error("cannot resume");
    }

    /**
     * Suspend execution of all threads but this thread.
     */
    static void pauseAll()
    {
        synchronized (Thread.classinfo)
        {
            if (nthreads > 1)
            {
                thread_id thisid = GetCurrentThreadId();

                for (int i = 0; i < allThreadsDim; i++)
                {
                    Thread t = allThreads[i];
                    if (t && t.id != thisid && t.state == TS.RUNNING)
                        t.pause();
                }
            }
        }
    }

    /**
     * Resume execution of all paused threads.
     */
    static void resumeAll()
    {
        synchronized (Thread.classinfo)
        {
            if (nthreads > 1)
            {
                thread_id thisid = GetCurrentThreadId();

                for (int i = 0; i < allThreadsDim; i++)
                {
                    Thread t = allThreads[i];
                    if (t && t.id != thisid && t.state == TS.RUNNING)
                        t.resume();
                }
            }
        }
    }

    /**
     * Give up the remainder of this thread's time slice.
     */
    static void yield()
    {
	Sleep(0);
    }

    /**
     *
     */
    static uint nthreads = 1;

  private:

    static uint allThreadsDim;
    static Thread[0x400] allThreads;	// length matches value in C runtime

    TS state;
    int idx = -1;			// index into allThreads[]
    thread_id id;
    size_t stacksize = 0;

    int (*fp)(void *);
    void *arg;

    int delegate() dg;

    void error(string msg)
    {
	throw new ThreadError(msg);
    }


    /* ***********************************************
     * This is just a wrapper to interface between C rtl and Thread.run().
     */

    extern (Windows) static uint threadstart(void *p)
    {
	Thread t = cast(Thread)p;
	int result;

	debug (thread) printf("Starting thread %d\n", t.idx);
	t.stackBottom = os_query_stackBottom();
	try
	{
	    result = t.run();
	}
	catch (Object o)
	{
	    fprintf(stderr, "Error: %.*s\n", o.toString());
	    result = 1;
	}

	debug (thread) printf("Ending thread %d\n", t.idx);
	t.state = TS.TERMINATED;
        synchronized (Thread.classinfo)
        {
            allThreads[t.idx] = null;
            t.idx = -1;
            nthreads--;
        }
	return result;
    }


    /**************************************
     * Create a Thread for global main().
     */

    public static void thread_init()
    {
	Thread t = new Thread();

	t.state = TS.RUNNING;
	t.id = GetCurrentThreadId();
	t.hdl = Thread.getCurrentThreadHandle();
	t.stackBottom = os_query_stackBottom();

	assert(!allThreads[0]);
	allThreads[0] = t;
	allThreadsDim = 1;
	t.idx = 0;
    }

    static ~this()
    {
	if (allThreadsDim)
	{
	    CloseHandle(allThreads[0].hdl);
	    allThreads[0].hdl = GetCurrentThread();
	}
    }
          
    /********************************************
     * Returns the handle of the current thread.
     * This is needed because GetCurrentThread() always returns -2 which
     * is a pseudo-handle representing the current thread.
     * The returned thread handle is a windows resource and must be explicitly
     * closed.
     * Many thanks to Justin (jhenzie@mac.com) for figuring this out
     * and providing the fix.
     */
    static thread_hdl getCurrentThreadHandle()
    {
	thread_hdl currentThread = GetCurrentThread();
	thread_hdl actualThreadHandle;

	//thread_hdl currentProcess = cast(thread_hdl)-1;
	thread_hdl currentProcess = GetCurrentProcess(); // http://www.digitalmars.com/drn-bin/wwwnews?D/21217


	uint access = cast(uint)0x00000002;

	DuplicateHandle(currentProcess, currentThread, currentProcess,
			 &actualThreadHandle, cast(uint)0, TRUE, access);

	return actualThreadHandle;
     }
}


/**********************************************
 * Determine "bottom" of stack (actually the top on Win32 systems).
 */

void *os_query_stackBottom()
{
    asm
    {
	naked			;
	mov	EAX,FS:4	;
	ret			;
    }
}

}

/* ================================ linux ================================= */

version (linux)
{

private import std.c.linux.linux;
private import std.c.linux.linuxextern;

alias uint pthread_t;
extern (C) alias void (*__sighandler_t)(int);

struct sigset_t
{
    uint __val[1024 / (8 * uint.sizeof)];
}

struct sigaction_t
{
    __sighandler_t sa_handler;
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)();
}

struct pthread_attr_t
{
    int __detachstate;
    int __schedpolicy;
    struct __schedparam
    {
	int __sched_priority;
    }
    int __inheritsched;
    int __scope;
    size_t __guardsize;
    int __stackaddr_set;
    void *__stackaddr;
    size_t __stacksize;
}

unittest
{
    assert(sigset_t.sizeof  == 128);
    assert(sigaction_t.sizeof == 140);
    assert(sem_t.sizeof == 16);
}

extern (C)
{
    int pthread_create(pthread_t*, void*, void* (*)(void*), void*);
    int pthread_join(pthread_t, void**);
    int pthread_kill(pthread_t, int);
    pthread_t pthread_self();
    int pthread_equal(pthread_t, pthread_t);
    int pthread_attr_init(pthread_attr_t*);
    int pthread_attr_setstacksize(pthread_attr_t *, size_t);
    int pthread_cancel(pthread_t);
    int pthread_setcancelstate(int, int*);
    int pthread_setcanceltype(int, int*);
    int sched_yield();
    int sigfillset(sigset_t*);
    int sigdelset(sigset_t*, int);
    int sigaction(int, sigaction_t*, sigaction_t*);
    int sigsuspend(sigset_t*);

    enum
    {
	PTHREAD_CANCEL_ENABLE,
	PTHREAD_CANCEL_DISABLE
    }

    enum
    {
	PTHREAD_CANCEL_DEFERRED,
	PTHREAD_CANCEL_ASYNCHRONOUS
    }
}

class ThreadError : Error
{
    this(string s)
    {
	super("Thread error: " ~ s);
    }
}

class Thread
{
    // The optional stacksize parameter default value of 0 will cause threads
    //  to be created with the default pthread size - Dave Fladebo
    this(size_t stacksize = 0)
    {
	init(stacksize);
    }

    this(int (*fp)(void *), void *arg, size_t stacksize = 0)
    {
	this.fp = fp;
	this.arg = arg;
	init(stacksize);
    }

    this(int delegate() dg, size_t stacksize = 0)
    {
	this.dg = dg;
	init(stacksize);
    }

    ~this()
    {
	pthread_cond_destroy(&waitCond);
	pthread_mutex_destroy(&waitMtx);
        if (state != TS.FINISHED)
            pthread_detach(id);
    }

    pthread_t id;
    void* stackBottom;
    void* stackTop;

    void start()
    {
	if (state != TS.INITIAL)
	    error("already started");

	synchronized (Thread.classinfo)
	{
	    for (int i = 0; 1; i++)
	    {
		if (i == allThreads.length)
		    error("too many threads");
		if (!allThreads[i])
		{   allThreads[i] = this;
		    idx = i;
		    if (i >= allThreadsDim)
			allThreadsDim = i + 1;
		    break;
		}
	    }
	    nthreads++;
	}

	state = TS.RUNNING;
	//printf("creating thread x%x\n", this);
	//result = pthread_create(&id, null, &threadstart, this);
	// Create with thread attributes to allow non-default stack size - Dave Fladebo
	int result = pthread_create(&id, &threadAttrs, &threadstart, cast(void*)this);
	if (result)
	{   state = TS.FINISHED;
	    synchronized (Thread.classinfo) allThreads[idx] = null;
	    idx = -1;
	    error("failed to start");	// BUG: should report errno
	}
	//printf("t = x%x, id = %d\n", this, id);
    }

    int run()
    {
	if (fp)
	    return fp(arg);
	else if (dg)
	    return dg();
	assert(0);
    }

    void wait()
    {
	if (isSelf)
	    error("wait on self");

	if (state != TS.FINISHED)
	{
	    void *value;

	    int result = pthread_join(id, &value);
            state = TS.FINISHED;
	    if (result)
		error("failed to wait");
	}
    }

    void wait(uint milliseconds)
    {
	// Implemented for POSIX systems by Dave Fladebo
	if (isSelf)
	    error("wait on self");
	if (state != TS.FINISHED)
	{
	    timespec ts; 
	    timeval  tv;

	    pthread_mutex_lock(&waitMtx);
	    gettimeofday(&tv, null);
	    ts.tv_sec = cast(__time_t)tv.tv_sec + cast(__time_t)(milliseconds / 1_000);
	    ts.tv_nsec = (tv.tv_usec * 1_000) + ((milliseconds % 1_000) * 1_000_000);
	    if (ts.tv_nsec > 1_000_000_000)
	    {
		ts.tv_sec += 1;
		ts.tv_nsec -= 1_000_000_000;
	    }
	    if (pthread_cond_timedwait(&waitCond, &waitMtx, &ts))
	    {
		int oldstate, oldtype;
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
		pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);

		if (pthread_cancel(id))     // thread was not completed in the timeout period, cancel it
		{
		    pthread_mutex_unlock(&waitMtx);
		    error("cannot terminate thread via timed wait");
		}

		pthread_setcancelstate(oldstate, null);
		pthread_setcanceltype(oldtype, null);

		state = TS.TERMINATED;
                synchronized (Thread.classinfo)
                {
                    allThreads[idx] = null;
                    idx = -1;
                    nthreads--;
                }

		pthread_mutex_unlock(&waitMtx);
	    }
	    else
	    {
		pthread_mutex_unlock(&waitMtx);
		wait();	// condition has been signalled as complete (see threadstart()), terminate normally
	    }
	}
    }

    enum TS
    {
	INITIAL,        // created
	RUNNING,        // running
	TERMINATED,     // execution finished
        FINISHED        // pthread_join()'ed
    }

    TS getState()
    {
	return state;
    }

    enum PRIORITY
    {
	INCREASE,
	DECREASE,
	IDLE,
	CRITICAL
    }

    void setPriority(PRIORITY p)
    {
	/+ not implemented
	int nPriority;

	switch (p)
	{
	    case PRIORITY.INCREASE:
		nPriority = THREAD_PRIORITY_ABOVE_NORMAL;
		break;
	    case PRIORITY.DECREASE:
		nPriority = THREAD_PRIORITY_BELOW_NORMAL;
		break;
	    case PRIORITY.IDLE:
		nPriority = THREAD_PRIORITY_IDLE;
		break;
	    case PRIORITY.CRITICAL:
		nPriority = THREAD_PRIORITY_TIME_CRITICAL;
		break;
	}

	if (SetThreadPriority(hdl, nPriority) == THREAD_PRIORITY_ERROR_RETURN)
	    error("set priority");
	+/
    }

    int isSelf()
    {
	//printf("id = %d, self = %d\n", id, pthread_self());
	return pthread_equal(pthread_self(), id);
    }

    static Thread getThis()
    {
	//printf("getThis(), allThreadsDim = %d\n", allThreadsDim);
        pthread_t id = pthread_self();
        //printf("id = %d\n", id);
        for (int i = 0; i < allThreadsDim; i++)
        {
            Thread t = allThreads[i];
            //printf("allThreads[%d] = x%x, id = %d\n", i, t, (t ? t.id : 0));
            if (t && pthread_equal(id, t.id))
            {
                return t;
            }
        }
	printf("didn't find it\n");
	assert(null);
    }

    static Thread[] getAll()
    {
	synchronized (Thread.classinfo) return allThreads[0 .. allThreadsDim];
    }

    void pause()
    {
	if (state == TS.RUNNING)
	{
	    if (pthread_kill(id, SIGUSR1))
		error("cannot pause");
	    else
		sem_wait(&flagSuspend);	// wait for acknowledgement
	}
	else
	    error("cannot pause");
    }

    void resume()
    {
	if (state == TS.RUNNING)
	{
	    if (pthread_kill(id, SIGUSR2))
		error("cannot resume");
	}
	else
	    error("cannot resume");
    }

    static void pauseAll()
    {
        synchronized (Thread.classinfo)
        {
            if (nthreads > 1)
            {
                pthread_t thisid = pthread_self();
                int npause = 0;

                for (int i = 0; i < allThreadsDim; i++)
                {
                    Thread t = allThreads[i];
                    if (t && !pthread_equal(thisid, t.id) && t.state == TS.RUNNING)
                    {
                        if (pthread_kill(t.id, SIGUSR1))
                            t.error("cannot pause");
                        else
                            npause++;	// count of paused threads
                    }
                }

                // Wait for each paused thread to acknowledge
                while (npause--)
                {
                    sem_wait(&flagSuspend);
                }
            }
        }
    }

    static void resumeAll()
    {
        synchronized (Thread.classinfo)
        {
            if (nthreads > 1)
            {
                pthread_t thisid = pthread_self();

                for (int i = 0; i < allThreadsDim; i++)
                {
                    Thread t = allThreads[i];
                    if (t && t.id != thisid && t.state == TS.RUNNING)
                        t.resume();
                }
            }
        }
    }

    static void yield()
    {
	sched_yield();
    }

    static uint nthreads = 1;

  private:

    static uint allThreadsDim;

    // Set max to Windows equivalent for compatibility.
    // pthread_create will fail gracefully if stack limit
    // is reached prior to allThreads max.
    static Thread[0x400] allThreads;

    static sem_t flagSuspend;

    TS state;
    int idx = -1;			// index into allThreads[]
    int flags = 0;

    pthread_attr_t threadAttrs;
    pthread_mutex_t waitMtx;
    pthread_cond_t waitCond;

    int (*fp)(void *);
    void *arg;

    int delegate() dg;

    void error(string msg)
    {
	throw new ThreadError(msg);
    }

    void init(size_t stackSize)
    {
	// set to default values regardless
	// passing this as the 2nd arg. for pthread_create()
	// w/o setting an attribute is equivalent to passing null.
	pthread_attr_init(&threadAttrs);
	if (stackSize > 0)
	{
	    if (pthread_attr_setstacksize(&threadAttrs,stackSize))
		error("cannot set stack size");
	}

	if (pthread_mutex_init(&waitMtx, null))
	    error("cannot initialize wait mutex");

	if (pthread_cond_init(&waitCond, null))
	    error("cannot initialize wait condition");
    }

    /************************************************
     * This is just a wrapper to interface between C rtl and Thread.run().
     */

    extern (C) static void *threadstart(void *p)
    {
	Thread t = cast(Thread)p;
	int result;

	debug (thread) printf("Starting thread x%x (%d)\n", t, t.idx);

	// Need to set t.id here, because thread is off and running
	// before pthread_create() sets it.
	t.id = pthread_self();

	t.stackBottom = getESP();
	try
	{
	    if(t.state == TS.RUNNING)
		pthread_cond_signal(&t.waitCond);     // signal the wait condition (see the timed wait function)
	    result = t.run();
	}
	catch (Object o)
	{
	    fprintf(stderr, "Error: %.*s\n", o.toString());
	    result = 1;
	}

	debug (thread) printf("Ending thread %d\n", t.idx);
	t.state = TS.TERMINATED;
        synchronized (Thread.classinfo)
        {
            allThreads[t.idx] = null;
            t.idx = -1;
            nthreads--;
        }
	return cast(void*)result;
    }


    /**************************************
     * Create a Thread for global main().
     */

    public static void thread_init()
    {
	Thread t = new Thread();

	t.state = TS.RUNNING;
	t.id = pthread_self();

	version (none)
	{
	    // See discussion: http://autopackage.org/forums/viewtopic.php?t=22
	    static void** libc_stack_end;

	    if (libc_stack_end == libc_stack_end.init)
	    {
		void* handle = dlopen(null, RTLD_NOW);
		libc_stack_end = cast(void **)dlsym(handle, "__libc_stack_end");
		dlclose(handle);
	    }
	    t.stackBottom = *libc_stack_end;
	}
	else
	{
	    t.stackBottom = cast(void*)__libc_stack_end;
	}

	assert(!allThreads[0]);
	allThreads[0] = t;
	allThreadsDim = 1;
	t.idx = 0;

	/* Install signal handlers so we can suspend/resume threads
	 */

	int result;
	sigaction_t sigact;
	result = sigfillset(&sigact.sa_mask);
	if (result)
	    goto Lfail;
	sigact.sa_handler = &pauseHandler;
	result = sigaction(SIGUSR1, &sigact, null);
	if (result)
	    goto Lfail;
	sigact.sa_handler = &resumeHandler;
	result = sigaction(SIGUSR2, &sigact, null);
	if (result)
	    goto Lfail;

	result = sem_init(&flagSuspend, 0, 0);
	if (result)
	    goto Lfail;

	return;

      Lfail:
	t.error("cannot initialize threads");
    }

    /**********************************
     * This gets called when a thread gets SIGUSR1.
     */

    extern (C) static void pauseHandler(int sig)
    {	int result;

	// Save all registers on the stack so they'll be scanned by the GC
	asm
	{
	    pusha	;
	}

	assert(sig == SIGUSR1);

	sigset_t sigmask;
	result = sigfillset(&sigmask);
	assert(result == 0);
	result = sigdelset(&sigmask, SIGUSR2);
	assert(result == 0);

	Thread t = getThis();
	t.stackTop = getESP();
	t.flags &= ~1;
	// Release the semaphore _after_ stackTop is set
	sem_post(&flagSuspend);
	while (1)
	{
	    sigsuspend(&sigmask);	// suspend until SIGUSR2
	    if (t.flags & 1)		// ensure it was resumeHandler()
		break;
	}

	// Restore all registers
	asm
	{
	    popa	;
	}
    }

    /**********************************
     * This gets called when a thread gets SIGUSR2.
     */

    extern (C) static void resumeHandler(int sig)
    {
	Thread t = getThis();

	t.flags |= 1;
    }

    public static void* getESP()
    {
	asm
	{   naked	;
	    mov EAX,ESP	;
	    ret		;
	}
    }
}


}

