diff --git a/README.html b/README.html
index 9ba601e8..949a61ed 100644
--- a/README.html
+++ b/README.html
@@ -977,6 +977,7 @@ Files are no longer hosted at LuaForge.
Fix return value for table.remove() and table.insert() (fixes issue #39)
Fix aliasing issue for some multiple assignments from varargs return values (fixes issue #38)
Let os.getenv() return System.getenv() values first for JSE, then fall back to properties (fixes issue #25)
+Improve collection of orphaned coroutines when yielding from debug hook functions.
diff --git a/src/core/org/luaj/vm2/LuaThread.java b/src/core/org/luaj/vm2/LuaThread.java
index 1575a600..2286d3c7 100644
--- a/src/core/org/luaj/vm2/LuaThread.java
+++ b/src/core/org/luaj/vm2/LuaThread.java
@@ -63,10 +63,14 @@ public class LuaThread extends LuaValue {
public static int coroutine_count = 0;
- /** Interval at which to check for lua threads that are no longer referenced.
- * This can be changed by Java startup code if desired.
+ /** Polling interval, in milliseconds, which each thread uses while waiting to
+ * return from a yielded state to check if the lua threads is no longer
+ * referenced and therefore should be garbage collected.
+ * A short polling interval for many threads will consume server resources.
+ * Orphaned threads cannot be detected and collected unless garbage
+ * collection is run. This can be changed by Java startup code if desired.
*/
- static long thread_orphan_check_interval = 30000;
+ public static long thread_orphan_check_interval = 5000;
public static final int STATUS_INITIAL = 0;
public static final int STATUS_SUSPENDED = 1;
@@ -83,12 +87,9 @@ public class LuaThread extends LuaValue {
public final State state;
public static final int MAX_CALLSTACK = 256;
-
- /** Interval to check for LuaThread dereferencing. */
- public static int GC_INTERVAL = 30000;
/** Thread-local used by DebugLib to store debugging state.
- * This is ano opaque value that should not be modified by applications. */
+ * This is an opaque value that should not be modified by applications. */
public Object callstack;
public final Globals globals;
@@ -244,6 +245,15 @@ public class LuaThread extends LuaValue {
this.result = LuaValue.NONE;
}
}
+
+ public synchronized void set_inhook(boolean value) {
+ LuaThread t = (LuaThread) this.lua_thread.get();
+ if (t == null) {
+ this.status = STATUS_DEAD;
+ throw new OrphanedThread();
+ }
+ t.inhook = value;
+ }
}
}
diff --git a/src/core/org/luaj/vm2/lib/CoroutineLib.java b/src/core/org/luaj/vm2/lib/CoroutineLib.java
index 8790c602..f17e063c 100644
--- a/src/core/org/luaj/vm2/lib/CoroutineLib.java
+++ b/src/core/org/luaj/vm2/lib/CoroutineLib.java
@@ -62,8 +62,6 @@ import org.luaj.vm2.Varargs;
*/
public class CoroutineLib extends TwoArgFunction {
- static long thread_orphan_check_interval = 30000;
-
static int coroutine_count = 0;
Globals globals;
diff --git a/src/core/org/luaj/vm2/lib/DebugLib.java b/src/core/org/luaj/vm2/lib/DebugLib.java
index 17001b3a..f78cb5b0 100644
--- a/src/core/org/luaj/vm2/lib/DebugLib.java
+++ b/src/core/org/luaj/vm2/lib/DebugLib.java
@@ -388,61 +388,55 @@ public class DebugLib extends TwoArgFunction {
}
public void onCall(LuaFunction f) {
- LuaThread t = globals.running;
- if (t.inhook) return;
+ if (globals.running.inhook) return;
callstack().onCall(f);
- if (t.hookcall && t.hookfunc != null)
- callHook(CALL, NIL);
+ callHook(globals.running.hookfunc, CALL, NIL);
}
public void onCall(LuaClosure c, Varargs varargs, LuaValue[] stack) {
- LuaThread t = globals.running;
- if (t.inhook) return;
+ if (globals.running.inhook) return;
callstack().onCall(c, varargs, stack);
- if (t.hookcall && t.hookfunc != null)
- callHook(CALL, NIL);
+ callHook(globals.running.hookfunc, CALL, NIL);
}
public void onInstruction(int pc, Varargs v, int top) {
- LuaThread t = globals.running;
- if (t.inhook) return;
+ if (globals.running.inhook) return;
callstack().onInstruction(pc, v, top);
- if (t.hookfunc == null) return;
- if (t.hookcount > 0)
- if (++t.bytecodes % t.hookcount == 0)
- callHook(COUNT, NIL);
- if (t.hookline) {
+ if (globals.running.hookfunc == null) return;
+ if (globals.running.hookcount > 0)
+ if (++globals.running.bytecodes % globals.running.hookcount == 0)
+ callHook(globals.running.hookfunc, COUNT, NIL);
+ if (globals.running.hookline) {
int newline = callstack().currentline();
- if ( newline != t.lastline ) {
- t.lastline = newline;
- callHook(LINE, LuaValue.valueOf(newline));
+ if ( newline != globals.running.lastline ) {
+ globals.running.lastline = newline;
+ callHook(globals.running.hookfunc, LINE, LuaValue.valueOf(newline));
}
}
}
public void onReturn() {
- LuaThread t = globals.running;
- if (t.inhook) return;
+ if (globals.running.inhook) return;
callstack().onReturn();
- if (t.hookcall && t.hookfunc != null)
- callHook(RETURN, NIL);
+ callHook(globals.running.hookfunc, RETURN, NIL);
}
public String traceback(int level) {
return callstack().traceback(level);
}
- void callHook(LuaValue type, LuaValue arg) {
- LuaThread t = globals.running;
- t.inhook = true;
+ void callHook(LuaValue hookfunc, LuaValue type, LuaValue arg) {
+ if (hookfunc == null) return;
+ LuaThread.State state = globals.running.state;
+ state.set_inhook(true);
try {
- t.hookfunc.call(type, arg);
+ hookfunc.call(type, arg);
} catch (LuaError e) {
throw e;
} catch (RuntimeException e) {
throw new LuaError(e);
} finally {
- t.inhook = false;
+ state.set_inhook(false);
}
}