From b86e06ab1cd7c7543c931e9d82d707f3c4f3a20b Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Tue, 23 Oct 2007 21:24:49 +0000 Subject: [PATCH] Coroutine implementation based on Java Threads. --- .../lua/addon/luacompat/CoroutinesLib.java | 23 ++- src/main/java/lua/value/LThread.java | 141 +++++++++++------- src/test/res/coroutines.lua | 5 +- src/test/res/coroutines.luac | Bin 3549 -> 3537 bytes 4 files changed, 103 insertions(+), 66 deletions(-) diff --git a/src/addon/java/lua/addon/luacompat/CoroutinesLib.java b/src/addon/java/lua/addon/luacompat/CoroutinesLib.java index be2f6ef4..669bd028 100644 --- a/src/addon/java/lua/addon/luacompat/CoroutinesLib.java +++ b/src/addon/java/lua/addon/luacompat/CoroutinesLib.java @@ -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; diff --git a/src/main/java/lua/value/LThread.java b/src/main/java/lua/value/LThread.java index 781754dd..cea853d4 100644 --- a/src/main/java/lua/value/LThread.java +++ b/src/main/java/lua/value/LThread.java @@ -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,68 +85,61 @@ public class LThread extends LValue { */ public void resumeFrom(VM vm, int nargs) { - if ( status == STATUS_DEAD ) { - vm.settop(0); - vm.pushboolean(false); - vm.pushstring("cannot resume dead coroutine"); - return; - } - - // set prior thread to normal status while t - LThread prior = running; - try { - // set our status to running - running = this; - if ( prior != null ) - prior.status = STATUS_NORMAL; - status = STATUS_RUNNING; - - // copy args in - if ( threadVm.cc < 0 ) { - vm.xmove(threadVm, nargs); - threadVm.prepStackCall(); - } else { - threadVm.settop(0); - vm.xmove(threadVm, nargs); + synchronized ( this ) { + if ( status == STATUS_DEAD ) { + vm.settop(0); + vm.pushboolean(false); + vm.pushstring("cannot resume dead coroutine"); + return; } - - // run this vm until it yields - while ( threadVm.cc >= 0 && status == STATUS_RUNNING ) - threadVm.exec(); - // copy return values from yielding stack state - vm.settop(0); - vm.pushboolean(true); - if ( threadVm.cc >= 0 ) { - threadVm.xmove(vm, threadVm.gettop() - 1); - } else { - threadVm.base = 0; - threadVm.xmove(vm, threadVm.gettop()); - } - - } catch ( Throwable t ) { - status = STATUS_DEAD; - vm.settop(0); - vm.pushboolean(false); - vm.pushstring("thread: "+t); - - } finally { - // previous thread is now running - running = prior; - if ( running != null ) - running.status = STATUS_RUNNING; - - // check if thread is actually dead - if ( threadVm.cc < 0 ) + // set prior thread to normal status while we are running + LThread prior = running; + try { + // set our status to running + if ( prior != null ) + prior.status = STATUS_NORMAL; + running = this; + status = STATUS_RUNNING; + + // copy args in + 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 + this.notify(); + this.wait(); + + // copy return values from yielding stack state + vm.settop(0); + vm.pushboolean(true); + if ( threadVm.cc >= 0 ) { + threadVm.xmove(vm, threadVm.gettop() - 1); + } else { + threadVm.base = 0; + threadVm.xmove(vm, threadVm.gettop()); + } + + } catch ( Throwable t ) { status = STATUS_DEAD; - + vm.settop(0); + vm.pushboolean(false); + vm.pushstring("thread: "+t); + this.notify(); + + } finally { + // previous thread is now running again + running = prior; + } } } - public boolean yield() { - if ( status == STATUS_RUNNING ) - status = STATUS_SUSPENDED; - return true; - } } diff --git a/src/test/res/coroutines.lua b/src/test/res/coroutines.lua index 2cfa3dbc..caa55b55 100644 --- a/src/test/res/coroutines.lua +++ b/src/test/res/coroutines.lua @@ -10,7 +10,7 @@ function foo (a) print("foo", a) return coroutine.yield(2*a) end - + co = coroutine.create(function (a,b) print("co-body", a, b) local r = foo(a+1) @@ -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) diff --git a/src/test/res/coroutines.luac b/src/test/res/coroutines.luac index 7f11dd3093562c565f3695793fdbe0e9310edc65..c1b52d17efdfe09915037103a3a58d2a3ea502f7 100644 GIT binary patch delta 204 zcmcaBeNlSDeNLeUMh3