Coroutine implementation based on Java Threads.

This commit is contained in:
James Roseborough
2007-10-23 21:24:49 +00:00
parent c311c31a74
commit b86e06ab1c
4 changed files with 103 additions and 66 deletions

View File

@@ -10,14 +10,21 @@ import lua.value.LThread;
public class CoroutinesLib extends LFunction { public class CoroutinesLib extends LFunction {
private static final String[] NAMES = {
"loadlib",
"create",
"resume",
"running",
"status",
"wrap",
"yield",
"wrapped"
};
public static void install() { public static void install() {
LTable lib = new LTable(0,6); LTable lib = new LTable(0,6);
lib.put("create", new CoroutinesLib(1)); for ( int i=1; i<=6; i++ )
lib.put("resume", new CoroutinesLib(2)); lib.put(NAMES[i], new CoroutinesLib(i));
lib.put("running", new CoroutinesLib(3));
lib.put("status", new CoroutinesLib(4));
lib.put("wrap", new CoroutinesLib(5));
lib.put("yield", new CoroutinesLib(6));
GlobalState.getGlobalsTable().put("coroutine",lib); GlobalState.getGlobalsTable().put("coroutine",lib);
} }
@@ -34,6 +41,10 @@ public class CoroutinesLib extends LFunction {
this.thread = null; this.thread = null;
} }
public String toJavaString() {
return "coroutine."+NAMES[id];
}
private CoroutinesLib( int id, LThread thread ) { private CoroutinesLib( int id, LThread thread ) {
this.id = id; this.id = id;
this.thread = thread; this.thread = thread;

View File

@@ -5,7 +5,10 @@ import lua.StackState;
import lua.VM; import lua.VM;
import lua.io.Closure; import lua.io.Closure;
public class LThread extends LValue { /**
* Implementation of lua coroutines using Java Threads
*/
public class LThread extends LValue implements Runnable {
private static final int STATUS_SUSPENDED = 0; private static final int STATUS_SUSPENDED = 0;
private static final int STATUS_RUNNING = 1; private static final int STATUS_RUNNING = 1;
@@ -20,6 +23,7 @@ public class LThread extends LValue {
private int status = STATUS_SUSPENDED; private int status = STATUS_SUSPENDED;
private StackState threadVm; private StackState threadVm;
private Thread thread;
private static LThread running; private static LThread running;
@@ -46,6 +50,34 @@ public class LThread extends LValue {
return running; return running;
} }
public void run() {
synchronized ( this ) {
try {
threadVm.execute();
} finally {
status = STATUS_DEAD;
this.notify();
}
}
}
public boolean yield() {
synchronized ( this ) {
if ( status != STATUS_RUNNING )
throw new RuntimeException(this+" not running");
status = STATUS_SUSPENDED;
this.notify();
try {
this.wait();
status = STATUS_RUNNING;
} catch ( InterruptedException e ) {
status = STATUS_DEAD;
throw new RuntimeException(this+" "+e);
}
return false;
}
}
/** This needs to leave any values returned by yield in the coroutine /** This needs to leave any values returned by yield in the coroutine
* on the calling vm stack * on the calling vm stack
* @param vm * @param vm
@@ -53,6 +85,7 @@ public class LThread extends LValue {
*/ */
public void resumeFrom(VM vm, int nargs) { public void resumeFrom(VM vm, int nargs) {
synchronized ( this ) {
if ( status == STATUS_DEAD ) { if ( status == STATUS_DEAD ) {
vm.settop(0); vm.settop(0);
vm.pushboolean(false); vm.pushboolean(false);
@@ -60,27 +93,29 @@ public class LThread extends LValue {
return; return;
} }
// set prior thread to normal status while t // set prior thread to normal status while we are running
LThread prior = running; LThread prior = running;
try { try {
// set our status to running // set our status to running
running = this;
if ( prior != null ) if ( prior != null )
prior.status = STATUS_NORMAL; prior.status = STATUS_NORMAL;
running = this;
status = STATUS_RUNNING; status = STATUS_RUNNING;
// copy args in // copy args in
if ( threadVm.cc < 0 ) { if ( thread == null ) {
vm.xmove(threadVm, nargs); vm.xmove(threadVm, nargs);
threadVm.prepStackCall(); threadVm.prepStackCall();
thread = new Thread(this);
thread.start();
} else { } else {
threadVm.settop(0); threadVm.settop(0);
vm.xmove(threadVm, nargs); vm.xmove(threadVm, nargs);
} }
// run this vm until it yields // run this vm until it yields
while ( threadVm.cc >= 0 && status == STATUS_RUNNING ) this.notify();
threadVm.exec(); this.wait();
// copy return values from yielding stack state // copy return values from yielding stack state
vm.settop(0); vm.settop(0);
@@ -97,24 +132,14 @@ public class LThread extends LValue {
vm.settop(0); vm.settop(0);
vm.pushboolean(false); vm.pushboolean(false);
vm.pushstring("thread: "+t); vm.pushstring("thread: "+t);
this.notify();
} finally { } finally {
// previous thread is now running // previous thread is now running again
running = prior; running = prior;
if ( running != null ) }
running.status = STATUS_RUNNING;
// check if thread is actually dead
if ( threadVm.cc < 0 )
status = STATUS_DEAD;
} }
} }
public boolean yield() {
if ( status == STATUS_RUNNING )
status = STATUS_SUSPENDED;
return true;
}
} }

View File

@@ -46,7 +46,7 @@ exercise();
co = coroutine.create(function (a,b) co = coroutine.create(function (a,b)
print("co-body", a, b) print("co-body", a, b)
local statis,r = pcall( foo, a+1 ) local status,r = pcall( foo, a+1 )
print("co-body", status,r) print("co-body", status,r)
local r, s = coroutine.yield(a+b, a-b) local r, s = coroutine.yield(a+b, a-b)
print("co-body", r, s) print("co-body", r, s)
@@ -55,6 +55,7 @@ end)
exercise(); exercise();
-- wrap test -- wrap test
local g = coroutine.wrap(function (a,b) local g = coroutine.wrap(function (a,b)
print("co-body", a, b) print("co-body", a, b)

Binary file not shown.