Software /
code /
prosody
File
util/timer.lua @ 5877:615a0774e4cc
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.
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Wed, 30 Oct 2013 17:44:42 -0400 |
parent | 5776:bd0ff8ae98a8 |
child | 5878:2b1c0c0a2ea6 |
line wrap: on
line source
-- 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 "util.indexedbheap"; local log = require "util.logger".init("timer"); local server = require "net.server"; local math_min = math.min local math_huge = math.huge local get_time = require "socket".gettime; local t_insert = table.insert; local pairs = pairs; local type = type; local data = {}; local new_data = {}; module "timer" local _add_task; if not server.event then function _add_task(delay, callback) local current_time = get_time(); delay = delay + current_time; if delay >= current_time then t_insert(new_data, {delay, callback}); else local r = callback(current_time); if r and type(r) == "number" then return _add_task(r, callback); end end end server._addtimer(function() local current_time = get_time(); if #new_data > 0 then for _, d in pairs(new_data) do t_insert(data, d); end new_data = {}; end local next_time = math_huge; for i, d in pairs(data) do local t, callback = d[1], d[2]; if t <= current_time then data[i] = nil; local r = callback(current_time); if type(r) == "number" then _add_task(r, callback); next_time = math_min(next_time, r); end else next_time = math_min(next_time, t - current_time); end end return next_time; end); else local event = server.event; local event_base = server.event_base; local EVENT_LEAVE = (event.core and event.core.LEAVE) or -1; function _add_task(delay, callback) local event_handle; event_handle = event_base:addevent(nil, 0, function () local ret = callback(get_time()); if ret then return 0, ret; elseif event_handle then return EVENT_LEAVE; end end , delay); end end --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;