2009-01-16 12:56:54 +00:00
|
|
|
// This module provides an interface to the virtual threading API in
|
2009-01-20 08:29:15 +00:00
|
|
|
// Monster.
|
2009-01-16 12:56:54 +00:00
|
|
|
|
|
|
|
module monster.modules.threads;
|
|
|
|
|
|
|
|
import monster.monster;
|
2009-01-20 08:29:15 +00:00
|
|
|
import std.stdio;
|
2009-01-16 12:56:54 +00:00
|
|
|
|
|
|
|
const char[] moduleDef =
|
|
|
|
"singleton thread;
|
2009-01-20 08:29:15 +00:00
|
|
|
|
|
|
|
// Used to kill or pause our own or other threads.
|
|
|
|
idle kill();
|
2009-01-16 12:56:54 +00:00
|
|
|
idle pause();
|
2009-01-20 08:29:15 +00:00
|
|
|
|
|
|
|
// Get status information about a thread
|
|
|
|
native bool isScheduled();
|
|
|
|
native bool isPaused();
|
|
|
|
native bool isIdle();
|
|
|
|
native bool isDead();
|
|
|
|
bool isAlive() { return !isDead(); }
|
|
|
|
|
|
|
|
// Create a new (paused) thread for a given function
|
|
|
|
native thread create(char[] name);
|
|
|
|
|
|
|
|
// Schedule a (paused) thread to run the next frame
|
|
|
|
native restart();
|
|
|
|
|
|
|
|
// Call a (paused) thread directly - returns when the thread exits or
|
|
|
|
// calls an idle function.
|
|
|
|
idle resume();
|
|
|
|
|
|
|
|
// Wait for a thread to finish. Will not return until the thread is
|
|
|
|
// dead.
|
|
|
|
idle wait();
|
|
|
|
|
|
|
|
// Call a function as a thread
|
|
|
|
thread call(char[] name)
|
|
|
|
{
|
|
|
|
var t = create(name);
|
|
|
|
t.resume();
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start a function as a thread in the background
|
|
|
|
thread start(char[] name)
|
|
|
|
{
|
|
|
|
var t = create(name);
|
|
|
|
t.restart();
|
|
|
|
return t;
|
|
|
|
}
|
2009-01-16 12:56:54 +00:00
|
|
|
"; //"
|
|
|
|
|
2009-01-20 08:29:15 +00:00
|
|
|
MonsterObject *trdSing;
|
|
|
|
|
|
|
|
class Kill : IdleFunction
|
|
|
|
{
|
|
|
|
IS initiate(Thread *t)
|
|
|
|
{
|
|
|
|
auto mo = params.obj;
|
|
|
|
|
|
|
|
if(mo !is trdSing)
|
|
|
|
{
|
|
|
|
// Check if this is another thread
|
|
|
|
auto trd = getOwner(mo);
|
|
|
|
if(trd !is t)
|
|
|
|
{
|
|
|
|
// It is. Kill it explicitly and return.
|
|
|
|
trd.kill();
|
|
|
|
return IS.Return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's our own thread, tell the scheduler to kill it from
|
|
|
|
// the inside.
|
|
|
|
return IS.Kill;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Pause : IdleFunction
|
|
|
|
{
|
|
|
|
IS initiate(Thread *t)
|
|
|
|
{
|
|
|
|
auto mo = params.obj;
|
|
|
|
|
|
|
|
// Can only run on the singleton object
|
|
|
|
if(mo !is trdSing)
|
|
|
|
fail("Can only pause our own thread");
|
|
|
|
|
|
|
|
// Move the thread to the 'paused' list
|
|
|
|
t.moveTo(&scheduler.paused);
|
|
|
|
|
|
|
|
return IS.Manual;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Thread *getOwner() { return getOwner(params.obj); }
|
|
|
|
Thread *getOwner(MonsterObject *mo)
|
|
|
|
{
|
|
|
|
assert(mo !is null);
|
|
|
|
if(mo is trdSing)
|
|
|
|
fail("Cannot run this function on the global singleton thread.");
|
|
|
|
auto trd = cast(Thread*) mo.getExtra(_threadClass).vptr;
|
|
|
|
assert(trd !is null);
|
|
|
|
assert(!trd.isRunning || !trd.isPaused,
|
|
|
|
"thread cannot be running and paused at the same time");
|
|
|
|
return trd;
|
|
|
|
}
|
|
|
|
|
|
|
|
MonsterObject *createObj(Thread *trd)
|
|
|
|
{
|
|
|
|
assert(trd !is null);
|
|
|
|
auto mo = _threadClass.createObject();
|
|
|
|
mo.getExtra(_threadClass).vptr = trd;
|
|
|
|
return mo;
|
|
|
|
}
|
|
|
|
|
|
|
|
void create()
|
2009-01-16 12:56:54 +00:00
|
|
|
{
|
2009-01-20 08:29:15 +00:00
|
|
|
// Can only run on the singleton object
|
|
|
|
if(params.obj !is trdSing)
|
|
|
|
fail("Can only use create() on the global thread object.");
|
|
|
|
|
|
|
|
char[] name = stack.popString8();
|
|
|
|
|
|
|
|
assert(cthread !is null);
|
|
|
|
|
|
|
|
// This is a dirty hack that's only needed because we haven't
|
|
|
|
// implemented function pointers yet. Gets the caller object.
|
|
|
|
assert(cthread.fstack.list.length >= 2);
|
|
|
|
auto nd = cthread.fstack.cur;
|
|
|
|
nd = cthread.fstack.list.getNext(nd);
|
|
|
|
if(nd.getCls() is _threadClass)
|
|
|
|
nd = cthread.fstack.list.getNext(nd);
|
|
|
|
auto mo = nd.obj;
|
|
|
|
|
|
|
|
// Find the function
|
|
|
|
auto fn = mo.cls.findFunction(name);
|
2009-01-16 12:56:54 +00:00
|
|
|
|
2009-01-20 08:29:15 +00:00
|
|
|
if(fn.paramSize > 0)
|
|
|
|
fail("create(): function " ~ name ~ " cannot have parameters");
|
|
|
|
|
|
|
|
// Create a new thread
|
|
|
|
Thread *trd = Thread.getNew();
|
|
|
|
|
|
|
|
// Schedule the thread run the next frame
|
|
|
|
trd.pushFunc(fn, mo);
|
|
|
|
assert(trd.isPaused);
|
|
|
|
|
|
|
|
// This will mess with the stack frame though, so set it up
|
|
|
|
// correctly.
|
|
|
|
trd.fstack.cur.frame = stack.getStartInt(0);
|
|
|
|
cthread.fstack.restoreFrame();
|
|
|
|
|
|
|
|
stack.pushObject(createObj(trd));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resume is used to restore a thread that was previously paused. It
|
|
|
|
// will enter the thread immediately, like call. If you wish to run it
|
|
|
|
// later, use restart instead.
|
|
|
|
class Resume : IdleFunction
|
|
|
|
{
|
|
|
|
override:
|
|
|
|
IS initiate(Thread *t)
|
|
|
|
{
|
|
|
|
if(params.obj is trdSing)
|
|
|
|
fail("Cannot use resume() on our own thread.");
|
|
|
|
|
|
|
|
// Get the thread we're resuming
|
|
|
|
auto trd = getOwner();
|
|
|
|
|
|
|
|
if(trd is t)
|
|
|
|
fail("Cannot use resume() on our own thread.");
|
|
|
|
|
|
|
|
if(trd.isDead)
|
|
|
|
fail("Cannot resume a dead thread.");
|
|
|
|
|
|
|
|
if(!trd.isPaused)
|
|
|
|
fail("Can only use resume() on paused threads");
|
|
|
|
|
|
|
|
// Background the current thread. Move it to the pause list
|
|
|
|
// first, so background doesn't inadvertently delete it.
|
|
|
|
t.moveTo(&scheduler.paused);
|
|
|
|
t.background();
|
|
|
|
assert(!t.isDead);
|
|
|
|
|
|
|
|
// Reenter the thread
|
|
|
|
trd.reenter();
|
|
|
|
assert(cthread is null);
|
|
|
|
|
|
|
|
// Put the old thread in the forground again
|
|
|
|
t.foreground();
|
|
|
|
|
|
|
|
// Make the thread transient again
|
|
|
|
t.moveTo(&scheduler.transient);
|
|
|
|
|
|
|
|
// Return to sender
|
|
|
|
return IS.Return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void abort(Thread *t)
|
|
|
|
{
|
|
|
|
fail("Cannot abort thread while it is calling another thread");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Wait : IdleFunction
|
|
|
|
{
|
|
|
|
override:
|
|
|
|
IS initiate(Thread *t)
|
|
|
|
{
|
|
|
|
if(params.obj is trdSing)
|
|
|
|
fail("Cannot use wait on our own thread.");
|
|
|
|
|
|
|
|
// Get the thread we're resuming
|
|
|
|
auto trd = getOwner();
|
|
|
|
|
|
|
|
if(trd is t)
|
|
|
|
fail("Cannot use wait on our own thread.");
|
|
|
|
|
|
|
|
// Return immediately if the thread is dead
|
|
|
|
if(trd.isDead)
|
|
|
|
return IS.Return;
|
|
|
|
|
|
|
|
t.idleData.vptr = trd;
|
|
|
|
|
|
|
|
return IS.Poll;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasFinished(Thread *t)
|
|
|
|
{
|
|
|
|
return (cast(Thread*)t.idleData.vptr).isDead;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void restart()
|
|
|
|
{
|
|
|
|
auto trd = getOwner();
|
|
|
|
|
|
|
|
if(trd.isDead)
|
|
|
|
fail("Cannot restart a dead thread");
|
|
|
|
|
|
|
|
if(!trd.isPaused)
|
|
|
|
fail("Can only use restart() on paused threads");
|
|
|
|
|
|
|
|
// Move to the runlist
|
|
|
|
trd.moveTo(scheduler.runNext);
|
|
|
|
}
|
|
|
|
|
|
|
|
void isDead()
|
|
|
|
{ stack.pushBool(getOwner().isDead); }
|
|
|
|
|
|
|
|
void isIdle()
|
|
|
|
{ stack.pushBool(getOwner().fstack.isIdle); }
|
|
|
|
|
|
|
|
void isPaused()
|
|
|
|
{ stack.pushBool(getOwner().isPaused); }
|
|
|
|
|
|
|
|
void isScheduled()
|
|
|
|
{ stack.pushBool(getOwner().isScheduled); }
|
|
|
|
|
|
|
|
MonsterClass _threadClass;
|
|
|
|
|
|
|
|
void initThreadModule()
|
|
|
|
{
|
|
|
|
if(_threadClass !is null)
|
2009-01-16 12:56:54 +00:00
|
|
|
return;
|
|
|
|
|
2009-01-20 08:29:15 +00:00
|
|
|
_threadClass = new MonsterClass(MC.String, moduleDef, "thread");
|
|
|
|
trdSing = _threadClass.getSing();
|
|
|
|
|
|
|
|
_threadClass.bind("kill", new Kill);
|
|
|
|
_threadClass.bind("resume", new Resume);
|
|
|
|
_threadClass.bind("pause", new Pause);
|
|
|
|
_threadClass.bind("wait", new Wait);
|
|
|
|
|
|
|
|
_threadClass.bind("create", &create);
|
|
|
|
_threadClass.bind("restart", &restart);
|
|
|
|
|
|
|
|
_threadClass.bind("isDead", &isDead);
|
|
|
|
_threadClass.bind("isIdle", &isIdle);
|
|
|
|
_threadClass.bind("isPaused", &isPaused);
|
|
|
|
_threadClass.bind("isScheduled", &isScheduled);
|
2009-01-16 12:56:54 +00:00
|
|
|
}
|