util.timer: Updated to use util.indexedbheap to provide a more complete API. Timers can now be stopped or rescheduled. Callbacks are now pcall'd. Adding/removing timers from within timer callbacks works better. Optional parameter can be passed when creating timer which gets passed to callback, eliminating the need for closures in various timer uses. Timers are now much more lightweight.
authorWaqas Hussain <waqas20@gmail.com>
Wed, 30 Oct 2013 17:44:42 -0400
changeset 5877 615a0774e4cc
parent 5876 f62ad84811df
child 5878 2b1c0c0a2ea6
util.timer: Updated to use util.indexedbheap to provide a more complete API. Timers can now be stopped or rescheduled. Callbacks are now pcall'd. Adding/removing timers from within timer callbacks works better. Optional parameter can be passed when creating timer which gets passed to callback, eliminating the need for closures in various timer uses. Timers are now much more lightweight.
util/timer.lua
--- a/util/timer.lua	Wed Oct 30 17:30:35 2013 -0400
+++ b/util/timer.lua	Wed Oct 30 17:44:42 2013 -0400
@@ -6,6 +6,8 @@
 -- COPYING file in the source package for more information.
 --
 
+local indexedbheap = require "util.indexedbheap";
+local log = require "util.logger".init("timer");
 local server = require "net.server";
 local math_min = math.min
 local math_huge = math.huge
@@ -78,6 +80,60 @@
 	end
 end
 
-add_task = _add_task;
+--add_task = _add_task;
+
+local h = indexedbheap.create();
+local params = {};
+local next_time = nil;
+local _id, _callback, _now, _param;
+local function _call() return _callback(_now, _id, _param); end
+local function _traceback_handler(err) log("error", "Traceback[timer]: %s", traceback(tostring(err), 2)); end
+local function _on_timer(now)
+	local peek;
+	while true do
+		peek = h:peek();
+		if peek == nil or peek > now then break; end
+		local _;
+		_, _callback, _id = h:pop();
+		_now = now;
+		_param = params[id];
+		params[id] = nil;
+		--item(now, id, _param); -- FIXME pcall
+		local success, err = xpcall(_call, _traceback_handler);
+		if success and type(err) == "number" then
+			h:insert(_callback, err + now, _id); -- re-add
+		end
+	end
+	next_time = peek;
+	if peek ~= nil then
+		return peek - now;
+	end
+end
+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;
+		_add_task(next_time - current_time, on_timer);
+	end
+	return id;
+end
+function stop(id)
+	params[id] = nil;
+	return h:remove(id);
+end
+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 _M;