| /******************************************************************************* |
| * Copyright (c) 2007-2012 LuaJ. 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; |
| |
| /** |
| * Subclass of {@link LuaValue} that implements |
| * a lua coroutine thread using Java Threads. |
| * <p> |
| * A LuaThread is typically created in response to a scripted call to |
| * {@code coroutine.create()} |
| * <p> |
| * The threads must be initialized with the globals, so that |
| * the global environment may be passed along according to rules of lua. |
| * This is done via a call to {@link #setGlobals(LuaValue)} |
| * at some point during globals initialization. |
| * See {@link BaseLib} for additional documentation and example code. |
| * <p> |
| * The utility classes {@link JsePlatform} and {@link JmePlatform} |
| * see to it that this initialization is done properly. |
| * For this reason it is highly recommended to use one of these classes |
| * when initializing globals. |
| * <p> |
| * The behavior of coroutine threads matches closely the behavior |
| * of C coroutine library. However, because of the use of Java threads |
| * to manage call state, it is possible to yield from anywhere in luaj. |
| * <p> |
| * Each Java thread wakes up at regular intervals and checks a weak reference |
| * to determine if it can ever be resumed. If not, it throws |
| * {@link OrphanedThread} which is an {@link java.lang.Error}. |
| * Applications should not catch {@link OrphanedThread}, because it can break |
| * the thread safety of luaj. |
| * |
| * @see LuaValue |
| * @see JsePlatform |
| * @see JmePlatform |
| * @see CoroutineLib |
| */ |
| public class LuaThread extends LuaValue { |
| |
| public static LuaValue s_metatable; |
| |
| 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. |
| */ |
| static long thread_orphan_check_interval = 30000; |
| |
| public static final int STATUS_INITIAL = 0; |
| public static final int STATUS_SUSPENDED = 1; |
| public static final int STATUS_RUNNING = 2; |
| public static final int STATUS_NORMAL = 3; |
| public static final int STATUS_DEAD = 4; |
| public static final String[] STATUS_NAMES = { |
| "suspended", |
| "suspended", |
| "running", |
| "normal", |
| "dead",}; |
| |
| 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. */ |
| public Object callstack; |
| |
| public final Globals globals; |
| |
| /** Hook function control state used by debug lib. */ |
| public LuaValue hookfunc; |
| |
| /** Error message handler for this thread, if any. */ |
| public LuaValue errorfunc; |
| |
| public boolean hookline; |
| public boolean hookcall; |
| public boolean hookrtrn; |
| public int hookcount; |
| public boolean inhook; |
| public int lastline; |
| public int bytecodes; |
| |
| |
| /** Private constructor for main thread only */ |
| public LuaThread(Globals globals) { |
| state = new State(globals, this, null); |
| state.status = STATUS_RUNNING; |
| this.globals = globals; |
| } |
| |
| /** |
| * Create a LuaThread around a function and environment |
| * @param func The function to execute |
| */ |
| public LuaThread(Globals globals, LuaValue func) { |
| LuaValue.assert_(func != null, "function cannot be null"); |
| state = new State(globals, this, func); |
| this.globals = globals; |
| } |
| |
| public int type() { |
| return LuaValue.TTHREAD; |
| } |
| |
| public String typename() { |
| return "thread"; |
| } |
| |
| public boolean isthread() { |
| return true; |
| } |
| |
| public LuaThread optthread(LuaThread defval) { |
| return this; |
| } |
| |
| public LuaThread checkthread() { |
| return this; |
| } |
| |
| public LuaValue getmetatable() { |
| return s_metatable; |
| } |
| |
| public String getStatus() { |
| return STATUS_NAMES[state.status]; |
| } |
| |
| public boolean isMainThread() { |
| return this.state.function == null; |
| } |
| |
| public Varargs resume(Varargs args) { |
| final LuaThread.State s = this.state; |
| if (s.status > LuaThread.STATUS_SUSPENDED) |
| return LuaValue.varargsOf(LuaValue.FALSE, |
| LuaValue.valueOf("cannot resume "+(s.status==LuaThread.STATUS_DEAD? "dead": "non-suspended")+" coroutine")); |
| return s.lua_resume(this, args); |
| } |
| |
| public static class State implements Runnable { |
| private final Globals globals; |
| final WeakReference lua_thread; |
| public final LuaValue function; |
| Varargs args = LuaValue.NONE; |
| Varargs result = LuaValue.NONE; |
| String error = null; |
| public int status = LuaThread.STATUS_INITIAL; |
| |
| State(Globals globals, LuaThread lua_thread, LuaValue function) { |
| this.globals = globals; |
| this.lua_thread = new WeakReference(lua_thread); |
| this.function = function; |
| } |
| |
| public synchronized void run() { |
| try { |
| Varargs a = this.args; |
| this.args = LuaValue.NONE; |
| this.result = function.invoke(a); |
| } catch (Throwable t) { |
| this.error = t.getMessage(); |
| } finally { |
| this.status = LuaThread.STATUS_DEAD; |
| this.notify(); |
| } |
| } |
| |
| public synchronized Varargs lua_resume(LuaThread new_thread, Varargs args) { |
| LuaThread previous_thread = globals.running; |
| try { |
| globals.running = new_thread; |
| this.args = args; |
| if (this.status == STATUS_INITIAL) { |
| this.status = STATUS_RUNNING; |
| new Thread(this, "Coroutine-"+(++coroutine_count)).start(); |
| } else { |
| this.notify(); |
| } |
| if (previous_thread != null) |
| previous_thread.state.status = STATUS_NORMAL; |
| this.status = STATUS_RUNNING; |
| this.wait(); |
| return (this.error != null? |
| LuaValue.varargsOf(LuaValue.FALSE, LuaValue.valueOf(this.error)): |
| LuaValue.varargsOf(LuaValue.TRUE, this.result)); |
| } catch (InterruptedException ie) { |
| throw new OrphanedThread(); |
| } finally { |
| this.args = LuaValue.NONE; |
| this.result = LuaValue.NONE; |
| this.error = null; |
| globals.running = previous_thread; |
| if (previous_thread != null) |
| globals.running.state.status =STATUS_RUNNING; |
| } |
| } |
| |
| public synchronized Varargs lua_yield(Varargs args) { |
| try { |
| this.result = args; |
| this.status = STATUS_SUSPENDED; |
| this.notify(); |
| do { |
| this.wait(thread_orphan_check_interval); |
| if (this.lua_thread.get() == null) { |
| this.status = STATUS_DEAD; |
| throw new OrphanedThread(); |
| } |
| } while (this.status == STATUS_SUSPENDED); |
| return this.args; |
| } catch (InterruptedException ie) { |
| this.status = STATUS_DEAD; |
| throw new OrphanedThread(); |
| } finally { |
| this.args = LuaValue.NONE; |
| this.result = LuaValue.NONE; |
| } |
| } |
| } |
| |
| } |