Comparison

net/server_epoll.lua @ 9855:6c2370f17027

net.server_epoll: Optimize timer handling
author Kim Alvefur <zash@zash.se>
date Tue, 12 Mar 2019 23:13:51 +0100
parent 9853:9aea8dbb105d
child 9933:aac4c55721f9
comparison
equal deleted inserted replaced
9854:115b5e32d960 9855:6c2370f17027
4 -- This project is MIT/X11 licensed. Please see the 4 -- This project is MIT/X11 licensed. Please see the
5 -- COPYING file in the source package for more information. 5 -- COPYING file in the source package for more information.
6 -- 6 --
7 7
8 8
9 local t_sort = table.sort;
10 local t_insert = table.insert; 9 local t_insert = table.insert;
11 local t_remove = table.remove;
12 local t_concat = table.concat; 10 local t_concat = table.concat;
13 local setmetatable = setmetatable; 11 local setmetatable = setmetatable;
14 local tostring = tostring; 12 local tostring = tostring;
15 local pcall = pcall; 13 local pcall = pcall;
16 local type = type; 14 local type = type;
18 local pairs = pairs; 16 local pairs = pairs;
19 local log = require "util.logger".init("server_epoll"); 17 local log = require "util.logger".init("server_epoll");
20 local socket = require "socket"; 18 local socket = require "socket";
21 local luasec = require "ssl"; 19 local luasec = require "ssl";
22 local gettime = require "util.time".now; 20 local gettime = require "util.time".now;
21 local indexedbheap = require "util.indexedbheap";
23 local createtable = require "util.table".create; 22 local createtable = require "util.table".create;
24 local inet = require "util.net"; 23 local inet = require "util.net";
25 local inet_pton = inet.pton; 24 local inet_pton = inet.pton;
26 local _SOCKETINVALID = socket._SOCKETINVALID or -1; 25 local _SOCKETINVALID = socket._SOCKETINVALID or -1;
27 26
67 66
68 local fds = createtable(10, 0); -- FD -> conn 67 local fds = createtable(10, 0); -- FD -> conn
69 68
70 -- Timer and scheduling -- 69 -- Timer and scheduling --
71 70
72 local timers = {}; 71 local timers = indexedbheap.create();
73 72
74 local function noop() end 73 local function noop() end
75 local function closetimer(t) 74 local function closetimer(t)
76 t[1] = 0; 75 t[1] = 0;
77 t[2] = noop; 76 t[2] = noop;
78 end 77 timers:remove(t.id);
79 78 end
80 -- Set to true when timers have changed 79
81 local resort_timers = false; 80 local function reschedule(t, time)
81 t[1] = time;
82 timers:reprioritize(t.id, time);
83 end
82 84
83 -- Add absolute timer 85 -- Add absolute timer
84 local function at(time, f) 86 local function at(time, f)
85 local timer = { time, f, close = closetimer }; 87 local timer = { time, f, close = closetimer, reschedule = reschedule, id = nil };
86 t_insert(timers, timer); 88 timer.id = timers:insert(timer, time);
87 resort_timers = true;
88 return timer; 89 return timer;
89 end 90 end
90 91
91 -- Add relative timer 92 -- Add relative timer
92 local function addtimer(timeout, f) 93 local function addtimer(timeout, f)
95 96
96 -- Run callbacks of expired timers 97 -- Run callbacks of expired timers
97 -- Return time until next timeout 98 -- Return time until next timeout
98 local function runtimers(next_delay, min_wait) 99 local function runtimers(next_delay, min_wait)
99 -- Any timers at all? 100 -- Any timers at all?
100 if not timers[1] then 101 local now = gettime();
102 local peek = timers:peek();
103 while peek do
104
105 if peek > now then
106 next_delay = peek - now;
107 break;
108 end
109
110 local _, timer, id = timers:pop();
111 local ok, ret = pcall(timer[2], now);
112 if ok and type(ret) == "number" then
113 local next_time = now+ret;
114 timer[1] = next_time;
115 timers:insert(timer, next_time);
116 end
117
118 peek = timers:peek();
119 end
120 if peek == nil then
101 return next_delay; 121 return next_delay;
102 end 122 end
103 123
104 if resort_timers then 124 if next_delay < min_wait then
105 -- Sort earliest timers to the end 125 return min_wait;
106 t_sort(timers, function (a, b) return a[1] > b[1]; end); 126 end
107 resort_timers = false;
108 end
109
110 -- Iterate from the end and remove completed timers
111 for i = #timers, 1, -1 do
112 local timer = timers[i];
113 local t, f = timer[1], timer[2];
114 -- Get time for every iteration to increase accuracy
115 local now = gettime();
116 if t > now then
117 -- This timer should not fire yet
118 local diff = t - now;
119 if diff < next_delay then
120 next_delay = diff;
121 end
122 break;
123 end
124 local new_timeout = f(now);
125 if new_timeout then
126 -- Schedule for 'delay' from the time actually scheduled, not from now,
127 -- in order to prevent timer drift, unless it already drifted way out of sync.
128 if (t + new_timeout) > ( now - new_timeout ) then
129 timer[1] = t + new_timeout;
130 else
131 timer[1] = now + new_timeout;
132 end
133 resort_timers = true;
134 else
135 t_remove(timers, i);
136 end
137 end
138
139 if resort_timers or next_delay < min_wait then
140 -- Timers may be added from within a timer callback.
141 -- Those would not be considered for next_delay,
142 -- and we might sleep for too long, so instead
143 -- we return a shorter timeout so we can
144 -- properly sort all new timers.
145 next_delay = min_wait;
146 end
147
148 return next_delay; 127 return next_delay;
149 end 128 end
150 129
151 -- Socket handler interface 130 -- Socket handler interface
152 131
249 end 228 end
250 return 229 return
251 end 230 end
252 t = t or cfg.read_timeout; 231 t = t or cfg.read_timeout;
253 if self._readtimeout then 232 if self._readtimeout then
254 self._readtimeout[1] = gettime() + t; 233 self._readtimeout:reschedule(gettime() + t);
255 resort_timers = true;
256 else 234 else
257 self._readtimeout = addtimer(t, function () 235 self._readtimeout = addtimer(t, function ()
258 if self:on("readtimeout") then 236 if self:on("readtimeout") then
259 return cfg.read_timeout; 237 return cfg.read_timeout;
260 else 238 else
274 end 252 end
275 return 253 return
276 end 254 end
277 t = t or cfg.send_timeout; 255 t = t or cfg.send_timeout;
278 if self._writetimeout then 256 if self._writetimeout then
279 self._writetimeout[1] = gettime() + t; 257 self._writetimeout:reschedule(gettime() + t);
280 resort_timers = true;
281 else 258 else
282 self._writetimeout = addtimer(t, function () 259 self._writetimeout = addtimer(t, function ()
283 self:on("disconnect", "write timeout"); 260 self:on("disconnect", "write timeout");
284 self:destroy(); 261 self:destroy();
285 end); 262 end);