diff --git a/tool/net/help.txt b/tool/net/help.txt index 7d99d3b25..128a0feb7 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -554,6 +554,12 @@ HOOKS each time redbean reaps a child connection process using wait4(). This won't be called in uniprocess mode. + OnServerHeartbeat() + If this function is defined it'll be called from the main process + on each server heartbeat. The heartbeat interval is configurable + with ProgramHeartbeatInterval. If this hook is defined, then + `/.heartbeat.lua` is not called. + OnServerListen(socketdescriptor:int,serverip:int,serverport:int) → bool If this function is defined it'll be called from the main process before redbean starts listening on a port. This hook can be used @@ -1368,6 +1374,10 @@ FUNCTIONS as Content-Range and Date, which are abstracted by the transport layer. + ProgramHeartbeatInterval(milliseconds:int) + Sets the heartbeat interval (in milliseconds). 5000ms is the default + and 100ms is the minimum. + ProgramTimeout(milliseconds:int|seconds:int) Default timeout is 60000ms. Minimal value of timeout is 10(ms). Negative values (<0) sets the keepalive in seconds. diff --git a/tool/net/redbean.c b/tool/net/redbean.c index b9bba8c9b..c32594f34 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -150,7 +150,6 @@ STATIC_YOINK("ShowCrashReportsEarly"); #endif #define VERSION 0x02000f -#define HEARTBEAT 5000 /*ms*/ #define HASH_LOAD_FACTOR /* 1. / */ 4 #define MONITOR_MICROS 150000 #define READ(F, P, N) readv(F, &(struct iovec){P, N}, 1) @@ -418,6 +417,7 @@ static int sslpskindex; static int oldloglevel; static int messageshandled; static int sslticketlifetime; +static int heartbeatint = 5000; // ms static uint32_t clientaddrsize; static size_t zsize; @@ -4737,6 +4737,16 @@ static int LuaProgramUniprocess(lua_State *L) { return 1; } +static int LuaProgramHeartbeatInterval(lua_State *L) { + OnlyCallFromMainProcess(L, "ProgramHeartbeatInterval"); + if (!lua_isinteger(L, 1) && !lua_isnoneornil(L, 1)) { + return luaL_argerror(L, 1, "invalid heartbeat interval; integer expected"); + } + lua_pushinteger(L, heartbeatint); + if (lua_isinteger(L, 1)) heartbeatint = MAX(100, lua_tointeger(L, 1)); + return 1; +} + static int LuaProgramAddr(lua_State *L) { uint32_t ip; OnlyCallFromInitLua(L, "ProgramAddr"); @@ -5218,6 +5228,7 @@ static const luaL_Reg kLuaFuncs[] = { {"ProgramDirectory", LuaProgramDirectory}, // {"ProgramGid", LuaProgramGid}, // {"ProgramHeader", LuaProgramHeader}, // + {"ProgramHeartbeatInterval", LuaProgramHeartbeatInterval}, // {"ProgramLogBodies", LuaProgramLogBodies}, // {"ProgramLogMessages", LuaProgramLogMessages}, // {"ProgramLogPath", LuaProgramLogPath}, // @@ -5734,7 +5745,11 @@ static void HandleHeartbeat(void) { Reindex(); getrusage(RUSAGE_SELF, &shared->server); #ifndef STATIC - LuaRunAsset("/.heartbeat.lua", false); + if (IsHookDefined("OnServerHeartbeat")) { + CallSimpleHook("OnServerHeartbeat"); + } else { + LuaRunAsset("/.heartbeat.lua", false); + } CollectGarbage(); #endif for (i = 1; i < servers.n; ++i) { @@ -7066,7 +7081,7 @@ static int EventLoop(int ms) { EnterMeltdownMode(); lua_repl_unlock(); meltdown = false; - } else if ((t = nowl()) - lastheartbeat > HEARTBEAT / 1000.) { + } else if ((t = nowl()) - lastheartbeat > heartbeatint / 1000.) { lastheartbeat = t; HandleHeartbeat(); } else if (HandlePoll(ms) == -1) { @@ -7366,12 +7381,12 @@ void RedBean(int argc, char *argv[]) { } } #ifdef STATIC - EventLoop(HEARTBEAT); + EventLoop(heartbeatint); #else GetHostsTxt(); // for effect GetResolvConf(); // for effect if (daemonize || uniprocess || !linenoiseIsTerminal()) { - EventLoop(HEARTBEAT); + EventLoop(heartbeatint); } else if (IsWindows()) { CHECK_NE(-1, _spawn(WindowsReplThread, 0, &replth)); EventLoop(100);