diff --git a/src/eval.c b/src/eval.c index 7d5c40a53..f8fe8bf37 100644 --- a/src/eval.c +++ b/src/eval.c @@ -45,9 +45,6 @@ void ldbInit(void); void ldbDisable(client *c); void ldbEnable(client *c); void evalGenericCommandWithDebugging(client *c, int evalsha); -void luaLdbLineHook(lua_State *lua, lua_Debug *ar); -void ldbLog(sds entry); -void ldbLogRedisReply(char *reply); sds ldbCatStackValue(sds s, lua_State *lua, int idx); /* Lua context */ @@ -377,7 +374,6 @@ void evalGenericCommand(client *c, int evalsha) { char funcname[43]; long long numkeys; long long initial_server_dirty = server.dirty; - int delhook = 0, err; /* When we replicate whole scripts, we want the same PRNG sequence at * every call so that our PRNG is not affected by external state. */ @@ -443,22 +439,10 @@ void evalGenericCommand(client *c, int evalsha) { serverAssert(!lua_isnil(lua,-1)); } - /* Populate the argv and keys table accordingly to the arguments that - * EVAL received. */ - luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys); - luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys); - lctx.lua_cur_script = funcname + 2; scriptRunCtx rctx; scriptPrepareForRun(&rctx, lctx.lua_client, c, lctx.lua_cur_script); - - /* We must set it before we set the Lua hook, theoretically the - * Lua hook might be called wheneven we run any Lua instruction - * such as 'luaSetGlobalArray' and we want the rctx to be available - * each time the Lua hook is invoked. */ - luaSaveOnRegistry(lua, REGISTRY_RUN_CTX_NAME, &rctx); - if (!lctx.lua_replicate_commands) rctx.flags |= SCRIPT_EVAL_REPLICATION; /* This check is for EVAL_RO, EVALSHA_RO. We want to allow only read only commands */ if ((server.script_caller->cmd->proc == evalRoCommand || @@ -466,53 +450,11 @@ void evalGenericCommand(client *c, int evalsha) { rctx.flags |= SCRIPT_READ_ONLY; } - if (server.script_time_limit > 0 && ldb.active == 0) { - lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000); - delhook = 1; - } else if (ldb.active) { - lua_sethook(lctx.lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000); - delhook = 1; - } - - /* At this point whether this script was never seen before or if it was - * already defined, we can call it. We have zero arguments and expect - * a single return value. */ - err = lua_pcall(lua,0,1,-2); - + luaCallFunction(&rctx, lua, c->argv+3, numkeys, c->argv+3+numkeys, c->argc-3-numkeys, ldb.active); + lua_pop(lua,1); /* Remove the error handler. */ scriptResetRun(&rctx); - /* Perform some cleanup that we need to do both on error and success. */ - if (delhook) lua_sethook(lua,NULL,0,0); /* Disable hook */ lctx.lua_cur_script = NULL; - luaSaveOnRegistry(lua, REGISTRY_RUN_CTX_NAME, NULL); - - /* Call the Lua garbage collector from time to time to avoid a - * full cycle performed by Lua, which adds too latency. - * - * The call is performed every LUA_GC_CYCLE_PERIOD executed commands - * (and for LUA_GC_CYCLE_PERIOD collection steps) because calling it - * for every command uses too much CPU. */ - #define LUA_GC_CYCLE_PERIOD 50 - { - static long gc_count = 0; - - gc_count++; - if (gc_count == LUA_GC_CYCLE_PERIOD) { - lua_gc(lua,LUA_GCSTEP,LUA_GC_CYCLE_PERIOD); - gc_count = 0; - } - } - - if (err) { - addReplyErrorFormat(c,"Error running script (call to %s): %s\n", - funcname, lua_tostring(lua,-1)); - lua_pop(lua,2); /* Consume the Lua reply and remove error handler. */ - } else { - /* On success convert the Lua return value into Redis protocol, and - * send it to * the client. */ - luaReplyToRedisReply(c,rctx.c,lua); /* Convert and consume the reply. */ - lua_pop(lua,1); /* Remove the error handler. */ - } /* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless * we are sure that the script was already in the context of all the diff --git a/src/script_lua.c b/src/script_lua.c index c7206095c..fb3fa397b 100644 --- a/src/script_lua.c +++ b/src/script_lua.c @@ -57,6 +57,7 @@ static void redisProtocolToLuaType_Double(void *ctx, double d, const char *proto static void redisProtocolToLuaType_BigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len); static void redisProtocolToLuaType_VerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len); static void redisProtocolToLuaType_Attribute(struct ReplyParser *parser, void *ctx, size_t len, const char *proto); +static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua); /* * Save the give pointer on Lua registry, used to save the Lua context and @@ -497,7 +498,7 @@ static void luaSortArray(lua_State *lua) { /* Reply to client 'c' converting the top element in the Lua stack to a * Redis reply. As a side effect the element is consumed from the stack. */ -void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua) { +static void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua) { int t = lua_type(lua,-1); if (!lua_checkstack(lua, 4)) { @@ -1201,7 +1202,7 @@ void luaRegisterRedisAPI(lua_State* lua) { /* Set an array of Redis String Objects as a Lua array (table) stored into a * global variable. */ -void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec) { +static void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec) { int j; lua_newtable(lua); @@ -1256,7 +1257,7 @@ static int redis_math_randomseed (lua_State *L) { } /* This is the Lua script "count" hook that we use to detect scripts timeout. */ -void luaMaskCountHook(lua_State *lua, lua_Debug *ar) { +static void luaMaskCountHook(lua_State *lua, lua_Debug *ar) { UNUSED(ar); scriptRunCtx* rctx = luaGetFromRegistry(lua, REGISTRY_RUN_CTX_NAME); if (scriptInterrupt(rctx) == SCRIPT_KILL) { @@ -1273,3 +1274,65 @@ void luaMaskCountHook(lua_State *lua, lua_Debug *ar) { lua_error(lua); } } + +void luaCallFunction(scriptRunCtx* run_ctx, lua_State *lua, robj** keys, size_t nkeys, robj** args, size_t nargs, int debug_enabled) { + client* c = run_ctx->original_client; + int delhook = 0; + + /* We must set it before we set the Lua hook, theoretically the + * Lua hook might be called wheneven we run any Lua instruction + * such as 'luaSetGlobalArray' and we want the run_ctx to be available + * each time the Lua hook is invoked. */ + luaSaveOnRegistry(lua, REGISTRY_RUN_CTX_NAME, run_ctx); + + if (server.script_time_limit > 0 && !debug_enabled) { + lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000); + delhook = 1; + } else if (debug_enabled) { + lua_sethook(lua,luaLdbLineHook,LUA_MASKLINE|LUA_MASKCOUNT,100000); + delhook = 1; + } + + /* Populate the argv and keys table accordingly to the arguments that + * EVAL received. */ + luaSetGlobalArray(lua,"KEYS",keys,nkeys); + luaSetGlobalArray(lua,"ARGV",args,nargs); + + /* At this point whether this script was never seen before or if it was + * already defined, we can call it. We have zero arguments and expect + * a single return value. */ + int err = lua_pcall(lua,0,1,-2); + + /* Call the Lua garbage collector from time to time to avoid a + * full cycle performed by Lua, which adds too latency. + * + * The call is performed every LUA_GC_CYCLE_PERIOD executed commands + * (and for LUA_GC_CYCLE_PERIOD collection steps) because calling it + * for every command uses too much CPU. */ + #define LUA_GC_CYCLE_PERIOD 50 + { + static long gc_count = 0; + + gc_count++; + if (gc_count == LUA_GC_CYCLE_PERIOD) { + lua_gc(lua,LUA_GCSTEP,LUA_GC_CYCLE_PERIOD); + gc_count = 0; + } + } + + if (err) { + addReplyErrorFormat(c,"Error running script (call to %s): %s\n", + run_ctx->funcname, lua_tostring(lua,-1)); + lua_pop(lua,1); /* Consume the Lua reply and remove error handler. */ + } else { + /* On success convert the Lua return value into Redis protocol, and + * send it to * the client. */ + luaReplyToRedisReply(c, run_ctx->c, lua); /* Convert and consume the reply. */ + } + + /* Perform some cleanup that we need to do both on error and success. */ + if (delhook) lua_sethook(lua,NULL,0,0); /* Disable hook */ + + /* remove run_ctx from registry, its only applicable for the current script. */ + luaSaveOnRegistry(lua, REGISTRY_RUN_CTX_NAME, NULL); +} diff --git a/src/script_lua.h b/src/script_lua.h index 5ae9225bc..c18ad40bd 100644 --- a/src/script_lua.h +++ b/src/script_lua.h @@ -58,12 +58,9 @@ void luaRegisterRedisAPI(lua_State* lua); void luaEnableGlobalsProtection(lua_State *lua); -void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec); -void luaMaskCountHook(lua_State *lua, lua_Debug *ar); -void luaReplyToRedisReply(client *c, client* script_client, lua_State *lua); void luaSaveOnRegistry(lua_State* lua, const char* name, void* ptr); void* luaGetFromRegistry(lua_State* lua, const char* name); - +void luaCallFunction(scriptRunCtx* r_ctx, lua_State *lua, robj** keys, size_t nkeys, robj** args, size_t nargs, int debug_enabled); #endif /* __SCRIPT_LUA_H_ */ diff --git a/src/server.h b/src/server.h index 2b1ef1ce7..3511ff04b 100644 --- a/src/server.h +++ b/src/server.h @@ -2706,6 +2706,7 @@ int ldbRemoveChild(pid_t pid); void ldbKillForkedSessions(void); int ldbPendingChildren(void); sds luaCreateFunction(client *c, robj *body); +void luaLdbLineHook(lua_State *lua, lua_Debug *ar); void freeLuaScriptsAsync(dict *lua_scripts); int ldbIsEnabled(); void ldbLog(sds entry);