Implement an internal command to interrupt running Lua scripts

This inserts a debug hook that runs every 256 instructions by
default, which has a minimal impact on performance.
develop
lethosor 2015-05-24 19:06:01 -04:00
parent 957b894a80
commit 96f3bdafb4
5 changed files with 62 additions and 0 deletions

@ -7,6 +7,7 @@ DFHack Future
Scripts can be enabled with the built-in enable/disable commands
A new function, reqscript(), is available as a safer alternative to script_environment()
New internal commands
kill-lua: Interrupt running Lua scripts
New plugins
New scripts
burial: sets all unowned coffins to allow burial ("-pets" to allow pets too)

@ -2095,6 +2095,12 @@ Tools:
* ``sand``: Displays an indicator when sand is present in the currently-selected area, similar to the default clay/stone indicators.
* ``sticky``: Maintains the selected local area while navigating the world map
kill-lua
--------
Interrupts any currently-running Lua scripts. By default, scripts can only be
interrupted every 256 instructions. Use ``kill-lua force`` to interrupt
the next instruction.
petcapRemover
-------------
This plugin allows you to remove or raise the pet population cap. In vanilla

@ -753,6 +753,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
" cls - Clear the console.\n"
" fpause - Force DF to pause.\n"
" die - Force DF to close immediately\n"
" kill-lua - Stop an active Lua script\n"
" keybinding - Modify bindings of commands to keys\n"
" script FILENAME - Run the commands specified in a file.\n"
" sc-script - Automatically run specified scripts on state change events\n"
@ -870,6 +871,17 @@ command_result Core::runCommand(color_ostream &con, const std::string &first_, v
{
_exit(666);
}
else if(first == "kill-lua")
{
bool force = false;
for (auto it = parts.begin(); it != parts.end(); ++it)
{
if (*it == "force")
force = true;
}
if (!Lua::Interrupt(force))
con.printerr("Failed to register hook - use 'kill-lua force' to force\n");
}
else if(first == "script")
{
if(parts.size() == 1)

@ -24,6 +24,7 @@ distribution.
#include "Internal.h"
#include <csignal>
#include <string>
#include <vector>
#include <map>
@ -354,6 +355,35 @@ static int dfhack_lineedit(lua_State *S)
* Exception handling
*/
volatile std::sig_atomic_t lstop = 0;
static void interrupt_hook (lua_State *L, lua_Debug *ar);
static void interrupt_init (lua_State *L)
{
lua_sethook(L, interrupt_hook, LUA_MASKCOUNT, 256);
}
static void interrupt_hook (lua_State *L, lua_Debug *ar)
{
if (lstop)
{
lstop = 0;
interrupt_init(L); // Restore default settings if necessary
luaL_error(L, "interrupted!");
}
}
bool DFHack::Lua::Interrupt (bool force)
{
lua_State *L = Lua::Core::State;
if (L->hook != interrupt_hook && !force)
return false;
if (force)
lua_sethook(L, interrupt_hook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT, 1);
lstop = 1;
return true;
}
static int DFHACK_EXCEPTION_META_TOKEN = 0;
static void error_tostring(lua_State *L, bool keep_old = false)
@ -1561,6 +1591,8 @@ lua_State *DFHack::Lua::Open(color_ostream &out, lua_State *state)
if (!state)
state = luaL_newstate();
interrupt_init(state);
luaL_openlibs(state);
AttachDFGlobals(state);

@ -263,6 +263,17 @@ namespace DFHack {namespace Lua {
bool (*init)(color_ostream&, lua_State*, void*),
void *arg);
/**
* Attempt to interrupt the currently-executing lua function by raising a lua error
* from a lua debug hook, similar to how SIGINT is handled in the lua interpreter (depends/lua/src/lua.c).
* The flag set here will only be checked every 256 instructions by default.
* Returns false if another debug hook is set and 'force' is false.
*
* force: Overwrite any existing debug hooks and interrupt the next instruction
*/
DFHACK_EXPORT bool Interrupt (bool force=false);
/**
* Push utility functions
*/