From a93c05e053a76e42a157db3e59e4e264dc5281a0 Mon Sep 17 00:00:00 2001
From: Kim Alvefur <zash@zash.se>
Date: Fri, 8 Jan 2021 21:57:19 +0100
Subject: net.server_epoll: Ensure timers can't run more than once per tick

This makes sure that a timer that returns 0 (or less) does not prevent
runtimers() from completing, as well as making sure a timer added with
zero timeout from within a timer does not run until the next tick.

Thanks tmolitor
---
 net/server_epoll.lua | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

(limited to 'net')

diff --git a/net/server_epoll.lua b/net/server_epoll.lua
index 953bbb11..e2be448c 100644
--- a/net/server_epoll.lua
+++ b/net/server_epoll.lua
@@ -97,10 +97,10 @@ local function runtimers(next_delay, min_wait)
 	-- Any timers at all?
 	local now = gettime();
 	local peek = timers:peek();
+	local readd;
 	while peek do
 
 		if peek > now then
-			next_delay = peek - now;
 			break;
 		end
 
@@ -109,13 +109,29 @@ local function runtimers(next_delay, min_wait)
 		if ok and type(ret) == "number"  then
 			local next_time = now+ret;
 			timer[1] = next_time;
-			timers:insert(timer, next_time);
+			-- Delay insertion of timers to be re-added
+			-- so they don't get called again this tick
+			if readd then
+				readd[id] = timer;
+			else
+				readd = { [id] = timer };
+			end
 		end
 
 		peek = timers:peek();
 	end
+
+	if readd then
+		for _, timer in pairs(readd) do
+			timers:insert(timer, timer[2]);
+		end
+		peek = timers:peek();
+	end
+
 	if peek == nil then
 		return next_delay;
+	else
+		next_delay = peek - now;
 	end
 
 	if next_delay < min_wait then
-- 
cgit v1.2.3