Improve coroutine state logic including let unreferenced coroutines be garbage collected
This commit is contained in:
43
README.html
43
README.html
@@ -16,7 +16,7 @@
|
|||||||
Getting Started with LuaJ
|
Getting Started with LuaJ
|
||||||
|
|
||||||
</h1>
|
</h1>
|
||||||
James Roseborough, Ian Farmer, Version 2.0.2
|
James Roseborough, Ian Farmer, Version 2.0.3
|
||||||
<p>
|
<p>
|
||||||
<small>
|
<small>
|
||||||
Copyright © 2009-2010 Luaj.org.
|
Copyright © 2009-2010 Luaj.org.
|
||||||
@@ -106,7 +106,7 @@ in comparison with the standard C distribution.
|
|||||||
<td>16.794</td>
|
<td>16.794</td>
|
||||||
<td>11.274</td>
|
<td>11.274</td>
|
||||||
<td>Java</td>
|
<td>Java</td>
|
||||||
<td>java -cp luaj-jse-2.0.2.jar;bcel-5.2.jar lua <b>-b</b> fannkuch.lua 10</td></tr>
|
<td>java -cp luaj-jse-2.0.3.jar;bcel-5.2.jar lua <b>-b</b> fannkuch.lua 10</td></tr>
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
@@ -116,7 +116,7 @@ in comparison with the standard C distribution.
|
|||||||
<td>16.701</td>
|
<td>16.701</td>
|
||||||
<td>13.789</td>
|
<td>13.789</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>java -cp luaj-jse-2.0.2.jar lua <b>-j</b> fannkuch.lua 10</td></tr>
|
<td>java -cp luaj-jse-2.0.3.jar lua <b>-j</b> fannkuch.lua 10</td></tr>
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
@@ -126,7 +126,7 @@ in comparison with the standard C distribution.
|
|||||||
<td>36.894</td>
|
<td>36.894</td>
|
||||||
<td>15.163</td>
|
<td>15.163</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>java -cp luaj-jse-2.0.2.jar lua -n fannkuch.lua 10</td></tr>
|
<td>java -cp luaj-jse-2.0.3.jar lua -n fannkuch.lua 10</td></tr>
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
<td>lua</td>
|
<td>lua</td>
|
||||||
<td>5.1.4</td>
|
<td>5.1.4</td>
|
||||||
@@ -182,7 +182,7 @@ It is also faster than Java-lua implementations Jill, Kahlua, and Mochalua for a
|
|||||||
From the main distribution directory line type:
|
From the main distribution directory line type:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
java -cp lib/luaj-jse-2.0.2.jar lua examples/lua/hello.lua
|
java -cp lib/luaj-jse-2.0.3.jar lua examples/lua/hello.lua
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -197,8 +197,8 @@ You should see the following output:
|
|||||||
From the main distribution directory line type:
|
From the main distribution directory line type:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
java -cp lib/luaj-jse-2.0.2.jar luac examples/lua/hello.lua
|
java -cp lib/luaj-jse-2.0.3.jar luac examples/lua/hello.lua
|
||||||
java -cp lib/luaj-jse-2.0.2.jar lua luac.out
|
java -cp lib/luaj-jse-2.0.3.jar lua luac.out
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -210,9 +210,9 @@ The compiled output "luac.out" is lua bytecode and should run and produce the sa
|
|||||||
Luaj can compile to lua source code to Java source code:
|
Luaj can compile to lua source code to Java source code:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
java -cp lib/luaj-jse-2.0.2.jar lua2java -s examples/lua -d . hello.lua
|
java -cp lib/luaj-jse-2.0.3.jar lua2java -s examples/lua -d . hello.lua
|
||||||
javac -cp lib/luaj-jse-2.0.2.jar hello.java
|
javac -cp lib/luaj-jse-2.0.3.jar hello.java
|
||||||
java -cp "lib/luaj-jse-2.0.2.jar;." lua -l hello
|
java -cp "lib/luaj-jse-2.0.3.jar;." lua -l hello
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -223,7 +223,7 @@ There are no additional dependencies for compiling or running source-to-source c
|
|||||||
<p>
|
<p>
|
||||||
Lua scripts can also be run directly in this mode without precompiling using the <em>lua</em> command with the <b><em>-j</em></b> option when run in JDK 1.5 or higher:
|
Lua scripts can also be run directly in this mode without precompiling using the <em>lua</em> command with the <b><em>-j</em></b> option when run in JDK 1.5 or higher:
|
||||||
<pre>
|
<pre>
|
||||||
java -cp lib/luaj-jse-2.0.2.jar lua -j examples/lua/hello.lua
|
java -cp lib/luaj-jse-2.0.3.jar lua -j examples/lua/hello.lua
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h2>Compile lua bytecode to java bytecode</h2>
|
<h2>Compile lua bytecode to java bytecode</h2>
|
||||||
@@ -233,8 +233,8 @@ Luaj can compile lua sources or binaries directly to java bytecode if the bcel l
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
ant bcel-lib
|
ant bcel-lib
|
||||||
java -cp "lib/luaj-jse-2.0.2.jar;lib/bcel-5.2.jar" luajc -s examples/lua -d . hello.lua
|
java -cp "lib/luaj-jse-2.0.3.jar;lib/bcel-5.2.jar" luajc -s examples/lua -d . hello.lua
|
||||||
java -cp "lib/luaj-jse-2.0.2.jar;." lua -l hello
|
java -cp "lib/luaj-jse-2.0.3.jar;." lua -l hello
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -245,7 +245,7 @@ but the compiled classes must be in the class path at runtime, unless runtime ji
|
|||||||
<p>
|
<p>
|
||||||
Lua scripts can also be run directly in this mode without precompiling using the <em>lua</em> command with the <b><em>-b</em></b> option and providing the <em>bcel</em> library in the class path:
|
Lua scripts can also be run directly in this mode without precompiling using the <em>lua</em> command with the <b><em>-b</em></b> option and providing the <em>bcel</em> library in the class path:
|
||||||
<pre>
|
<pre>
|
||||||
java -cp "lib/luaj-jse-2.0.2.jar;lib/bcel-5.2.jar" lua -b examples/lua/hello.lua
|
java -cp "lib/luaj-jse-2.0.3.jar;lib/bcel-5.2.jar" lua -b examples/lua/hello.lua
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
@@ -270,7 +270,7 @@ A simple example may be found in
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You must include the library <b>lib/luaj-jse-2.0.2.jar</b> in your class path.
|
You must include the library <b>lib/luaj-jse-2.0.3.jar</b> in your class path.
|
||||||
|
|
||||||
<h2>Run a script in a MIDlet</h2>
|
<h2>Run a script in a MIDlet</h2>
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ A simple example may be found in
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You must include the library <b>lib/luaj-jme-2.0.2.jar</b> in your midlet jar.
|
You must include the library <b>lib/luaj-jme-2.0.3.jar</b> in your midlet jar.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
An ant script to build and run the midlet is in
|
An ant script to build and run the midlet is in
|
||||||
@@ -325,7 +325,7 @@ The standard use of JSR-223 scripting engines may be used:
|
|||||||
All standard aspects of script engines including compiled statements should be supported.
|
All standard aspects of script engines including compiled statements should be supported.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You must include the library <b>lib/luaj-jse-2.0.2.jar</b> in your class path.
|
You must include the library <b>lib/luaj-jse-2.0.3.jar</b> in your class path.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
A working example may be found in
|
A working example may be found in
|
||||||
@@ -337,7 +337,7 @@ To compile and run it using Java 1.6 or higher:
|
|||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
javac examples/jse/ScriptEngineSample.java
|
javac examples/jse/ScriptEngineSample.java
|
||||||
java -cp "lib/luaj-jse-2.0.2.jar;examples/jse" ScriptEngineSample
|
java -cp "lib/luaj-jse-2.0.3.jar;examples/jse" ScriptEngineSample
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h2>Excluding the lua bytecode compiler</h2>
|
<h2>Excluding the lua bytecode compiler</h2>
|
||||||
@@ -501,7 +501,7 @@ The following lua script will open a swiing frame on Java SE:
|
|||||||
<p>
|
<p>
|
||||||
See a longer sample in <em>examples/lua/swingapp.lua</em> for details, or try running it using:
|
See a longer sample in <em>examples/lua/swingapp.lua</em> for details, or try running it using:
|
||||||
<pre>
|
<pre>
|
||||||
java -cp lib/luaj-jse-2.0.2.jar lua examples/lua/swingapp.lua
|
java -cp lib/luaj-jse-2.0.3.jar lua examples/lua/swingapp.lua
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -729,8 +729,11 @@ and LuaForge:
|
|||||||
<li>Enhance javadoc, put it <a href="docs/api/index.html">in distribution</a> and <a href="http://luaj.sourceforge.net/api/2.0/index.html">on line</a></li>
|
<li>Enhance javadoc, put it <a href="docs/api/index.html">in distribution</a> and <a href="http://luaj.sourceforge.net/api/2.0/index.html">on line</a></li>
|
||||||
<li>Major refactor of luajava type coercion logic, improve method selection.</li>
|
<li>Major refactor of luajava type coercion logic, improve method selection.</li>
|
||||||
<li>Add lib/luaj-sources-2.0.2.jar for easier integration into an IDE such as Netbeans </li>
|
<li>Add lib/luaj-sources-2.0.2.jar for easier integration into an IDE such as Netbeans </li>
|
||||||
|
<tr valign="top"><td> <b>2.0.3</b></td><td><ul>
|
||||||
|
<li>Improve coroutine state logic including let unreferenced coroutines be garbage collected </li>
|
||||||
</ul></td></tr>
|
</ul></td></tr>
|
||||||
</table>
|
</table></td></tr></table>
|
||||||
|
|
||||||
<h2>Known Issues</h2>
|
<h2>Known Issues</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>debug code may not be completely removed by some obfuscators
|
<li>debug code may not be completely removed by some obfuscators
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2007 LuaJ. All rights reserved.
|
* Copyright (c) 2007-2011 LuaJ. All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,6 +21,9 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package org.luaj.vm2;
|
package org.luaj.vm2;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
import org.luaj.vm2.lib.DebugLib;
|
import org.luaj.vm2.lib.DebugLib;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,7 +59,7 @@ import org.luaj.vm2.lib.DebugLib;
|
|||||||
* @see JmePlatform
|
* @see JmePlatform
|
||||||
* @see CoroutineLib
|
* @see CoroutineLib
|
||||||
*/
|
*/
|
||||||
public class LuaThread extends LuaValue implements Runnable {
|
public class LuaThread extends LuaValue {
|
||||||
|
|
||||||
public static LuaValue s_metatable;
|
public static LuaValue s_metatable;
|
||||||
|
|
||||||
@@ -69,31 +72,36 @@ public class LuaThread extends LuaValue implements Runnable {
|
|||||||
"suspended",
|
"suspended",
|
||||||
"running",
|
"running",
|
||||||
"normal",
|
"normal",
|
||||||
"dead" };
|
"dead",
|
||||||
|
"error" };
|
||||||
|
|
||||||
private int status = STATUS_SUSPENDED;
|
private int status = STATUS_SUSPENDED;
|
||||||
|
|
||||||
private Thread thread;
|
private JavaThread thread;
|
||||||
private LuaValue env;
|
private LuaValue env;
|
||||||
private LuaValue func;
|
private LuaValue func;
|
||||||
private Varargs args;
|
|
||||||
public LuaValue err;
|
|
||||||
|
|
||||||
|
/** Field to hold state of error condition during debug hook function calls. */
|
||||||
|
public LuaValue err;
|
||||||
|
|
||||||
public static final int MAX_CALLSTACK = 256;
|
public static final int MAX_CALLSTACK = 256;
|
||||||
public final LuaFunction[] callstack = new LuaFunction[MAX_CALLSTACK];
|
public final LuaFunction[] callstack = new LuaFunction[MAX_CALLSTACK];
|
||||||
public int calls = 0;
|
public int calls = 0;
|
||||||
|
|
||||||
private static final LuaThread mainthread = new LuaThread();
|
private static final LuaThread main_thread = new LuaThread();
|
||||||
|
|
||||||
// state of running thread including call stack
|
// state of running thread including call stack
|
||||||
private static LuaThread running_thread = mainthread;
|
private static LuaThread running_thread = main_thread;
|
||||||
|
|
||||||
// thread-local used by DebugLib to store debugging state
|
/** Interval to check for LuaThread dereferencing. */
|
||||||
|
public static int GC_INTERVAL = 30000;
|
||||||
|
|
||||||
|
/** Thread-local used by DebugLib to store debugging state. */
|
||||||
public Object debugState;
|
public Object debugState;
|
||||||
|
|
||||||
|
/** Private constructor for main thread only */
|
||||||
LuaThread() {
|
private LuaThread() {
|
||||||
|
status = STATUS_RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,7 +163,7 @@ public class LuaThread extends LuaValue implements Runnable {
|
|||||||
* @return true if this is the main thread
|
* @return true if this is the main thread
|
||||||
*/
|
*/
|
||||||
public static boolean isMainThread(LuaThread r) {
|
public static boolean isMainThread(LuaThread r) {
|
||||||
return r == mainthread;
|
return r == main_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,43 +225,16 @@ public class LuaThread extends LuaValue implements Runnable {
|
|||||||
null;
|
null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
/** Yield the current thread with arguments
|
||||||
synchronized ( this ) {
|
|
||||||
try {
|
|
||||||
this.args = func.invoke(this.args);
|
|
||||||
status = STATUS_DEAD;
|
|
||||||
} catch ( Throwable t ) {
|
|
||||||
String msg = t.getMessage();
|
|
||||||
this.args = valueOf(msg!=null? msg: t.toString());
|
|
||||||
status = STATUS_ERROR;
|
|
||||||
} finally {
|
|
||||||
this.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Yield this thread with arguments
|
|
||||||
*
|
*
|
||||||
* @param args The arguments to send as return values to {@link #resume(Varargs)}
|
* @param args The arguments to send as return values to {@link #resume(Varargs)}
|
||||||
* @return {@link Varargs} provided as arguments to {@link #resume(Varargs)}
|
* @return {@link Varargs} provided as arguments to {@link #resume(Varargs)}
|
||||||
*/
|
*/
|
||||||
public Varargs yield(Varargs args) {
|
public static Varargs yield(Varargs args) {
|
||||||
synchronized ( this ) {
|
JavaThread t = running_thread.thread;
|
||||||
if ( status != STATUS_RUNNING )
|
if ( t == null )
|
||||||
error(this+" not running");
|
error("cannot yield main thread");
|
||||||
status = STATUS_SUSPENDED;
|
return t.yield(args);
|
||||||
this.args = args;
|
|
||||||
this.notify();
|
|
||||||
try {
|
|
||||||
this.wait();
|
|
||||||
status = STATUS_RUNNING;
|
|
||||||
return this.args;
|
|
||||||
} catch ( InterruptedException e ) {
|
|
||||||
status = STATUS_DEAD;
|
|
||||||
error( "thread interrupted" );
|
|
||||||
return NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Start or resume this thread
|
/** Start or resume this thread
|
||||||
@@ -262,43 +243,112 @@ public class LuaThread extends LuaValue implements Runnable {
|
|||||||
* @return {@link Varargs} provided as arguments to {@link #yield(Varargs)}
|
* @return {@link Varargs} provided as arguments to {@link #yield(Varargs)}
|
||||||
*/
|
*/
|
||||||
public Varargs resume(Varargs args) {
|
public Varargs resume(Varargs args) {
|
||||||
|
if ( status != STATUS_SUSPENDED )
|
||||||
|
return varargsOf(FALSE, valueOf("cannot resume "+STATUS_NAMES[status]+" coroutine"));
|
||||||
|
if ( thread == null )
|
||||||
|
thread = new JavaThread(this,func);
|
||||||
|
return thread.resume(this,args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper class which contains the java stack used by this coroutine,
|
||||||
|
* and which detects when the LuaThread has been collected and completes.
|
||||||
|
*/
|
||||||
|
private static final class JavaThread extends Thread {
|
||||||
|
private final WeakReference ref;
|
||||||
|
private final LuaValue func;
|
||||||
|
private Varargs args;
|
||||||
|
private boolean started;
|
||||||
|
private static int count;
|
||||||
|
private JavaThread(LuaThread lua_thread,LuaValue func) {
|
||||||
|
this.ref = new WeakReference(lua_thread);
|
||||||
|
this.func = func;
|
||||||
|
this.setDaemon(true);
|
||||||
|
this.setName("LuaThread-"+(++count));
|
||||||
|
}
|
||||||
|
public void run() {
|
||||||
|
synchronized ( this ) {
|
||||||
|
try {
|
||||||
|
this.args = func.invoke(this.args);
|
||||||
|
setStatus( STATUS_DEAD );
|
||||||
|
} catch ( Throwable t ) {
|
||||||
|
String msg = t.getMessage();
|
||||||
|
this.args = valueOf(msg!=null? msg: t.toString());
|
||||||
|
setStatus( STATUS_ERROR );
|
||||||
|
} finally {
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Varargs yield(Varargs args) {
|
||||||
|
synchronized ( this ) {
|
||||||
|
if ( getStatus() != STATUS_RUNNING )
|
||||||
|
error(this+" not running");
|
||||||
|
setStatus( STATUS_SUSPENDED );
|
||||||
|
this.args = args;
|
||||||
|
this.notify();
|
||||||
|
try {
|
||||||
|
while ( getStatus() == STATUS_SUSPENDED )
|
||||||
|
this.wait(GC_INTERVAL);
|
||||||
|
if ( null == this.ref.get() )
|
||||||
|
stop();
|
||||||
|
setStatus( STATUS_RUNNING );
|
||||||
|
return this.args;
|
||||||
|
} catch ( InterruptedException e ) {
|
||||||
|
setStatus( STATUS_DEAD );
|
||||||
|
error( "thread interrupted" );
|
||||||
|
return NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setStatus(int status) {
|
||||||
|
LuaThread lt = (LuaThread) ref.get();
|
||||||
|
if ( lt != null )
|
||||||
|
lt.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getStatus() {
|
||||||
|
LuaThread lt = (LuaThread) ref.get();
|
||||||
|
return lt != null? lt.status: STATUS_DEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Varargs resume(LuaThread lua_thread, Varargs args) {
|
||||||
|
|
||||||
synchronized ( this ) {
|
synchronized ( this ) {
|
||||||
if ( status == STATUS_DEAD ) {
|
|
||||||
return varargsOf(FALSE, valueOf("cannot resume dead coroutine"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// set prior thread to normal status while we are running
|
// set prior thread to normal status while we are running
|
||||||
LuaThread prior = running_thread;
|
LuaThread prior = running_thread;
|
||||||
try {
|
try {
|
||||||
// set our status to running
|
// set our status to running
|
||||||
prior.status = STATUS_NORMAL;
|
prior.status = STATUS_NORMAL;
|
||||||
running_thread = this;
|
running_thread = lua_thread;
|
||||||
this.status = STATUS_RUNNING;
|
running_thread.status = STATUS_RUNNING;
|
||||||
|
|
||||||
// copy args in
|
// copy args in
|
||||||
this.args = args;
|
this.args = args;
|
||||||
|
|
||||||
// start the thread
|
// start thread if not started alread
|
||||||
if ( thread == null ) {
|
if ( ! this.started ) {
|
||||||
thread = new Thread(this);
|
this.started = true;
|
||||||
thread.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// run this vm until it yields
|
// wait for thread to yield or finish
|
||||||
this.notify();
|
this.notify();
|
||||||
this.wait();
|
this.wait();
|
||||||
|
|
||||||
// copy return values from yielding stack state
|
// copy return values from yielding stack state
|
||||||
if ( status == STATUS_ERROR ) {
|
if ( lua_thread.status == STATUS_ERROR ) {
|
||||||
status = STATUS_DEAD;
|
lua_thread.status = STATUS_DEAD;
|
||||||
return varargsOf(FALSE, this.args);
|
return varargsOf(FALSE, this.args);
|
||||||
} else {
|
} else {
|
||||||
return varargsOf(TRUE, this.args);
|
return varargsOf(TRUE, this.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch ( Throwable t ) {
|
} catch ( Throwable t ) {
|
||||||
status = STATUS_DEAD;
|
lua_thread.status = STATUS_DEAD;
|
||||||
try {
|
try {
|
||||||
return varargsOf(FALSE, valueOf("thread: "+t));
|
return varargsOf(FALSE, valueOf("thread: "+t));
|
||||||
} finally {
|
} finally {
|
||||||
@@ -308,9 +358,9 @@ public class LuaThread extends LuaValue implements Runnable {
|
|||||||
} finally {
|
} finally {
|
||||||
// previous thread is now running again
|
// previous thread is now running again
|
||||||
running_thread = prior;
|
running_thread = prior;
|
||||||
prior.status = STATUS_RUNNING;
|
running_thread.status = STATUS_RUNNING;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,10 +99,7 @@ public class CoroutineLib extends VarArgFunction {
|
|||||||
return valueOf( args.checkthread(1).getStatus() );
|
return valueOf( args.checkthread(1).getStatus() );
|
||||||
}
|
}
|
||||||
case YIELD: {
|
case YIELD: {
|
||||||
final LuaThread r = LuaThread.getRunning();
|
return LuaThread.yield( args );
|
||||||
if ( LuaThread.isMainThread( r ) )
|
|
||||||
error("main thread can't yield");
|
|
||||||
return r.yield( args );
|
|
||||||
}
|
}
|
||||||
case WRAP: {
|
case WRAP: {
|
||||||
final LuaValue func = args.checkfunction(1);
|
final LuaValue func = args.checkfunction(1);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public class AllTests {
|
|||||||
vm.addTestSuite(MetatableTest.class);
|
vm.addTestSuite(MetatableTest.class);
|
||||||
vm.addTestSuite(LuaOperationsTest.class);
|
vm.addTestSuite(LuaOperationsTest.class);
|
||||||
vm.addTestSuite(StringTest.class);
|
vm.addTestSuite(StringTest.class);
|
||||||
|
vm.addTestSuite(LuaThreadTest.class);
|
||||||
suite.addTest(vm);
|
suite.addTest(vm);
|
||||||
|
|
||||||
// table tests
|
// table tests
|
||||||
|
|||||||
134
test/junit/org/luaj/vm2/LuaThreadTest.java
Normal file
134
test/junit/org/luaj/vm2/LuaThreadTest.java
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2011 Luaj.org. All rights reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
******************************************************************************/
|
||||||
|
package org.luaj.vm2;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.luaj.vm2.lib.OneArgFunction;
|
||||||
|
|
||||||
|
public class LuaThreadTest extends TestCase {
|
||||||
|
|
||||||
|
public void testMainThread() {
|
||||||
|
assertEquals( true, LuaThread.isMainThread( LuaThread.getRunning()) );
|
||||||
|
assertEquals( "running", LuaThread.getRunning().getStatus() );
|
||||||
|
assertEquals( LuaValue.FALSE, LuaThread.getRunning().resume(LuaValue.NONE).arg1() );
|
||||||
|
try {
|
||||||
|
LuaThread.yield(LuaThread.yield(LuaValue.NONE));
|
||||||
|
fail("did not throw lua error as expected");
|
||||||
|
} catch ( LuaError le ) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLuaThreadIsCollected() throws InterruptedException { System.out.println("testLuaThread - starting");
|
||||||
|
int originalInterval = LuaThread.GC_INTERVAL;
|
||||||
|
try {
|
||||||
|
LuaThread.GC_INTERVAL = 75;
|
||||||
|
TestRig rig = new TestRig();
|
||||||
|
assertEquals( "resumed 1 times, arg=test-arg", rig.resumeOnce() );
|
||||||
|
assertEquals( true, rig.isThreadReferenced() );
|
||||||
|
assertEquals( true, rig.isFunctionReferenced() );
|
||||||
|
assertEquals( true, rig.isArgReferenced() );
|
||||||
|
collectGarbage();
|
||||||
|
assertEquals( "resumed 2 times, arg=test-arg", rig.resumeOnce() );
|
||||||
|
assertEquals( true, rig.isThreadReferenced() );
|
||||||
|
assertEquals( true, rig.isFunctionReferenced() );
|
||||||
|
assertEquals( true, rig.isArgReferenced() );
|
||||||
|
Thread.sleep( 200 );
|
||||||
|
collectGarbage();
|
||||||
|
assertEquals( "resumed 3 times, arg=test-arg", rig.resumeOnce() );
|
||||||
|
assertEquals( true, rig.isThreadReferenced() );
|
||||||
|
assertEquals( true, rig.isFunctionReferenced() );
|
||||||
|
assertEquals( true, rig.isArgReferenced() );
|
||||||
|
|
||||||
|
// check that references are collected
|
||||||
|
// some time after lua thread is de-referenced
|
||||||
|
rig.weakenReference();
|
||||||
|
Thread.sleep( 200 );
|
||||||
|
collectGarbage();
|
||||||
|
assertEquals( false, rig.isThreadReferenced() );
|
||||||
|
assertEquals( false, rig.isFunctionReferenced() );
|
||||||
|
assertEquals( false, rig.isArgReferenced() );
|
||||||
|
} finally {
|
||||||
|
LuaThread.GC_INTERVAL = originalInterval;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestRig {
|
||||||
|
LuaThread luaThread;
|
||||||
|
final WeakReference luaRef;
|
||||||
|
final WeakReference funcRef;
|
||||||
|
final WeakReference argRef;
|
||||||
|
TestRig() {
|
||||||
|
LuaValue a = new LuaUserdata( "test-arg" );
|
||||||
|
LuaValue f = new TestFunction();
|
||||||
|
luaThread = new LuaThread( f, new LuaTable() );
|
||||||
|
luaRef = new WeakReference( luaThread );
|
||||||
|
funcRef = new WeakReference( f );
|
||||||
|
argRef = new WeakReference( a );
|
||||||
|
}
|
||||||
|
public String resumeOnce() {
|
||||||
|
LuaThread t = (LuaThread) luaRef.get();
|
||||||
|
LuaValue a = (LuaValue) argRef.get();
|
||||||
|
return t==null? "no ref to lua thread":
|
||||||
|
a==null? "no ref to arg":
|
||||||
|
t.resume(a).arg(2).toString();
|
||||||
|
}
|
||||||
|
public void weakenReference() {
|
||||||
|
luaThread = null;
|
||||||
|
}
|
||||||
|
public Object isThreadReferenced() {
|
||||||
|
return null != luaRef.get();
|
||||||
|
}
|
||||||
|
public Object isFunctionReferenced() {
|
||||||
|
return null != funcRef.get();
|
||||||
|
}
|
||||||
|
public Object isArgReferenced() {
|
||||||
|
return null != argRef.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestFunction extends OneArgFunction {
|
||||||
|
public LuaValue call(LuaValue arg) {
|
||||||
|
for ( int count=1; true; count++ ) {
|
||||||
|
LuaValue r = LuaValue.valueOf("resumed "+count+" times, arg="+arg);
|
||||||
|
Varargs v = LuaThread.yield( r );
|
||||||
|
arg = v.arg1();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void collectGarbage() {
|
||||||
|
Runtime rt = Runtime.getRuntime();
|
||||||
|
rt.gc();
|
||||||
|
try {
|
||||||
|
Thread.sleep(20);
|
||||||
|
rt.gc();
|
||||||
|
Thread.sleep(20);
|
||||||
|
} catch ( Exception e ) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
rt.gc();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,3 +95,31 @@ step(111,222,333)
|
|||||||
step()
|
step()
|
||||||
step(111)
|
step(111)
|
||||||
step(111,222,333)
|
step(111,222,333)
|
||||||
|
|
||||||
|
-- test loops in resume calls
|
||||||
|
b = coroutine.create( function( arg )
|
||||||
|
while ( true ) do
|
||||||
|
print( ' b-resumed', arg, b == coroutine.running() )
|
||||||
|
print( ' b-b', coroutine.status(b) )
|
||||||
|
print( ' b-c', coroutine.status(c) )
|
||||||
|
print( ' b-resume-b',coroutine.resume( b, 'b-arg-for-b' ) )
|
||||||
|
print( ' b-resume-c',coroutine.resume( c, 'b-arg-for-c' ) )
|
||||||
|
arg = coroutine.yield( 'b-rslt' )
|
||||||
|
end
|
||||||
|
end )
|
||||||
|
c = coroutine.create( function( arg )
|
||||||
|
for i=1,3 do
|
||||||
|
print( ' c-resumed', arg, c == coroutine.running() )
|
||||||
|
print( ' c-b', coroutine.status(b) )
|
||||||
|
print( ' c-c', coroutine.status(c) )
|
||||||
|
print( ' c-resume-b',coroutine.resume( b, 'b-arg-for-b' ) )
|
||||||
|
print( ' c-resume-c',coroutine.resume( c, 'b-arg-for-c' ) )
|
||||||
|
arg = coroutine.yield( 'c-rslt' )
|
||||||
|
end
|
||||||
|
end )
|
||||||
|
for i=1,3 do
|
||||||
|
print( 'main-b', coroutine.status(b) )
|
||||||
|
print( 'main-c', coroutine.status(c) )
|
||||||
|
print( 'main-resume-b',coroutine.resume( b, 'main-arg-for-b' ) )
|
||||||
|
print( 'main-resume-c',coroutine.resume( c, 'main-arg-for-c' ) )
|
||||||
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user