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

View File

@@ -5,7 +5,10 @@ import lua.StackState;
import lua.VM;
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_RUNNING = 1;
@@ -20,6 +23,7 @@ public class LThread extends LValue {
private int status = STATUS_SUSPENDED;
private StackState threadVm;
private Thread thread;
private static LThread running;
@@ -46,6 +50,34 @@ public class LThread extends LValue {
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
* on the calling vm stack
* @param vm
@@ -53,6 +85,7 @@ public class LThread extends LValue {
*/
public void resumeFrom(VM vm, int nargs) {
synchronized ( this ) {
if ( status == STATUS_DEAD ) {
vm.settop(0);
vm.pushboolean(false);
@@ -60,27 +93,29 @@ public class LThread extends LValue {
return;
}
// set prior thread to normal status while t
// set prior thread to normal status while we are running
LThread prior = running;
try {
// set our status to running
running = this;
if ( prior != null )
prior.status = STATUS_NORMAL;
running = this;
status = STATUS_RUNNING;
// copy args in
if ( threadVm.cc < 0 ) {
if ( thread == null ) {
vm.xmove(threadVm, nargs);
threadVm.prepStackCall();
thread = new Thread(this);
thread.start();
} else {
threadVm.settop(0);
vm.xmove(threadVm, nargs);
}
// run this vm until it yields
while ( threadVm.cc >= 0 && status == STATUS_RUNNING )
threadVm.exec();
this.notify();
this.wait();
// copy return values from yielding stack state
vm.settop(0);
@@ -97,24 +132,14 @@ public class LThread extends LValue {
vm.settop(0);
vm.pushboolean(false);
vm.pushstring("thread: "+t);
this.notify();
} finally {
// previous thread is now running
// previous thread is now running again
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)
print("co-body", a, b)
local statis,r = pcall( foo, a+1 )
local status,r = pcall( foo, a+1 )
print("co-body", status,r)
local r, s = coroutine.yield(a+b, a-b)
print("co-body", r, s)
@@ -55,6 +55,7 @@ end)
exercise();
-- wrap test
local g = coroutine.wrap(function (a,b)
print("co-body", a, b)

Binary file not shown.