1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local indexedbheap = require "prosody.util.indexedbheap";
local log = require "prosody.util.logger".init("timer");
local server = require "prosody.net.server";
local get_time = require "prosody.util.time".now
local type = type;
local debug_traceback = debug.traceback;
local tostring = tostring;
local xpcall = require "prosody.util.xpcall".xpcall;
local math_max = math.max;
local pairs = pairs;
if server.timer then
-- The selected net.server implements this API, so defer to that
return server.timer;
end
local _ENV = nil;
-- luacheck: std none
local _add_task = server.add_task;
local _server_timer;
local _active_timers = 0;
local h = indexedbheap.create();
local params = {};
local next_time = nil;
local function _traceback_handler(err) log("error", "Traceback[timer]: %s", debug_traceback(tostring(err), 2)); end
local function _on_timer(now)
local peek;
local readd;
while true do
peek = h:peek();
if peek == nil or peek > now then break; end
local _, callback, id = h:pop();
local param = params[id];
params[id] = nil;
--item(now, id, _param);
local success, err = xpcall(callback, _traceback_handler, now, id, param);
if success and type(err) == "number" then
if readd then
readd[id] = { callback, err + now };
else
readd = { [id] = { callback, err + now } };
end
params[id] = param;
end
end
if readd then
for id,timer in pairs(readd) do
h:insert(timer[1], timer[2], id);
end
peek = h:peek();
end
if peek ~= nil and _active_timers > 1 and peek == next_time then
-- Another instance of _on_timer already set next_time to the same value,
-- so it should be safe to not renew this timer event
peek = nil;
else
next_time = peek;
end
if peek then
-- peek is the time of the next event
return peek - now;
end
_active_timers = _active_timers - 1;
end
local function add_task(delay, callback, param)
local current_time = get_time();
local event_time = current_time + delay;
local id = h:insert(callback, event_time);
params[id] = param;
if next_time == nil or event_time < next_time then
next_time = event_time;
if _server_timer then
_server_timer:close();
_server_timer = nil;
else
_active_timers = _active_timers + 1;
end
_server_timer = _add_task(next_time - current_time, _on_timer);
end
return id;
end
local function stop(id)
params[id] = nil;
local result, item, result_sync = h:remove(id);
local peek = h:peek();
if peek ~= next_time and _server_timer then
next_time = peek;
_server_timer:close();
if next_time ~= nil then
_server_timer = _add_task(math_max(next_time - get_time(), 0), _on_timer);
end
end
return result, item, result_sync;
end
local function reschedule(id, delay)
local current_time = get_time();
local event_time = current_time + delay;
h:reprioritize(id, delay);
if next_time == nil or event_time < next_time then
next_time = event_time;
_add_task(next_time - current_time, _on_timer);
end
return id;
end
return {
add_task = add_task;
stop = stop;
reschedule = reschedule;
};
|