From cd9cdc496e1b5b5d762df63dabf21b953b05afd7 Mon Sep 17 00:00:00 2001 From: James Roseborough Date: Wed, 18 Mar 2015 03:16:05 +0000 Subject: [PATCH] Improve collection of orphaned coroutines when yielding from debug hook functions. --- README.html | 1 + src/core/org/luaj/vm2/LuaThread.java | 24 ++++++++--- src/core/org/luaj/vm2/lib/CoroutineLib.java | 2 - src/core/org/luaj/vm2/lib/DebugLib.java | 48 +++++++++------------ 4 files changed, 39 insertions(+), 36 deletions(-) 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); } }