Add utilities and sample code to load luaj in custom class loader for strong sandboxing, and use of orphaned threads.

This commit is contained in:
James Roseborough
2015-04-17 02:59:50 +00:00
parent 70f7859cee
commit b545646922
7 changed files with 513 additions and 5 deletions

View File

@@ -0,0 +1,53 @@
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.JsePlatform;
/** Example that continually launches coroutines, and illustrates how to make
* sure the orphaned coroutines are cleaned up properly.
*
* Main points:
* <ul><li>Each coroutine consumes one Java Thread while active or reference anywhere</li>
* <li>All references to a coroutine must be dropped for the coroutine to be collected</li>
* <li>Garbage collection must be run regularly to remove weak references to lua threads</li>
* <li>LuaThread.thread_orphan_check_interval must be short enough to find orphaned references quickly</li>
* </ul>
*/
public class CollectingOrphanedCoroutines {
// Script that launches coroutines over and over in a loop.
// Garbage collection is done periodically to find and remove orphaned threads.
// Coroutines yield out when they are done.
static String script =
"i,n = 0,0\n print(i)\n"
+ "f = function() n=n+1; coroutine.yield(false) end\n"
+ "while true do\n"
+ " local cor = coroutine.wrap(f)\n"
+ " cor()\n"
+ " i = i + 1\n"
+ " if i % 1000 == 0 then\n"
+ " collectgarbage()\n"
+ " print('threads:', i, 'executions:', n, collectgarbage('count'))\n"
+ " end\n"
+ "end\n";
public static void main(String[] args) throws InterruptedException {
// This timer controls how often each Java thread wakes up and checks if
// it has been orhaned or not. A large number here will produce a long
// delay between orphaning and colleciton, and a small number here will
// consumer resources polling for orphaned status if there are many threads.
LuaThread.thread_orphan_check_interval = 500;
// Should work with standard or debug globals.
Globals globals = JsePlatform.standardGlobals();
// Globals globals = JsePlatform.debugGlobals();
// Should work with plain compiler or lua-to-Java compiler.
// org.luaj.vm2.luajc.LuaJC.install(globals);;
// Load and run the script, which launches coroutines over and over forever.
LuaValue chunk = globals.load(script, "main");
chunk.call();
}
}

View File

@@ -0,0 +1,95 @@
import java.io.InputStream;
import java.io.Reader;
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.JsePlatform;
import org.luaj.vm2.server.Launcher;
import org.luaj.vm2.server.LuajClassLoader;
/** Example of using {@link LuajClassLoader} to launch scripts that are blocked from
* interfering with globals from other scripts including shared static metatables.
* <P>
* This technique is useful in a server environment to expose the full set of
* lua features to each script, while preventing scripts from interfering with
* each other.
* <P>
* Because each Launch gets its own {@link LuajClassLoader}, it should be possible
* to include the debug library, or let scripts manipulate shared metatables,
* or luajava, which otherwise present challenges in a server environment.
* <P>
*/
public class SampleUsingClassLoader {
/** Script that manipulates the shared string metatable.
* When loaded by the {@link LuajClassLoader} via a {@link Launcher}
* created by that class, each instance of {@link LuajClassLoader} will
* have a completely separate version of all static variables.
*/
static String script =
"print('args:', ...)\n" +
"print('abc.foo', ('abc').foo)\n" +
"getmetatable('abc').__index.foo = function() return 'bar' end\n" +
"print('abc.foo', ('abc').foo)\n" +
"print('abc:foo()', ('abc'):foo())\n" +
"return math.pi\n";
public static void main(String[] s) throws Exception {
// The default launcher used standard globals.
RunUsingDefaultLauncher();
RunUsingDefaultLauncher();
// Example using custom launcher class that instantiates debug globals.
RunUsingCustomLauncherClass();
RunUsingCustomLauncherClass();
}
static void RunUsingDefaultLauncher() throws Exception {
Launcher launcher = LuajClassLoader.NewLauncher();
// starts with pristine Globals including all luaj static variables.
print(launcher.launch(script, new Object[] { "--------" }));
// reuses Globals and static variables from previous step.
print(launcher.launch(script, new Object[] {}));
}
static void RunUsingCustomLauncherClass() throws Exception {
Launcher launcher = LuajClassLoader.NewLauncher(MyLauncher.class);
// starts with pristine Globals including all luaj static variables.
print(launcher.launch(script, new Object[] { "=========" }));
// reuses Globals and static variables from previous step.
print(launcher.launch(script, new Object[] { "" }));
}
/** Example of Launcher implementation performing specialized launching.
* When loaded by the {@link LuajClassLoader} all luaj classes will be loaded
* for each instance of the {@link Launcher} and not interfere with other
* classes loaded by other instances.
*/
public static class MyLauncher implements Launcher {
Globals g;
public MyLauncher() {
g = JsePlatform.debugGlobals();
// ... plus any other customization of the user environment
}
public Object[] launch(String script, Object[] arg) {
LuaValue chunk = g.load(script, "main");
return new Object[] { chunk.call(LuaValue.valueOf(arg[0].toString())) };
}
public Object[] launch(InputStream script, Object[] arg) {
LuaValue chunk = g.load(script, "main", "bt", g);
return new Object[] { chunk.call(LuaValue.valueOf(arg[0].toString())) };
}
public Object[] launch(Reader script, Object[] arg) {
LuaValue chunk = g.load(script, "main");
return new Object[] { chunk.call(LuaValue.valueOf(arg[0].toString())) };
}
}
/** Print the return values as strings. */
private static void print(Object[] return_values) {
for (int i =0; i<return_values.length; ++i)
System.out.println("Return value " + return_values[i]);
}
}