Coroutine implementation based on Java Threads.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
Reference in New Issue
Block a user