summaryrefslogtreecommitdiff
path: root/xmake/core/base/timer.lua
blob: 98eef80a3f3aae2cf360af1085f0a2574d901afd (plain)
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
--!A cross-platform build utility based on Lua
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author      ruki
-- @file        timer.lua
--

-- load modules
local heap   = require("base/heap")
local object = require("base/object")

-- define module: timer
local timer  = timer or object()

-- tostring(timer)
function timer:__tostring()
    return string.format("<timer: %s>", self:name())
end

-- get all timer tasks
function timer:_tasks()
    return self._TASKS
end

-- post timer task after delay and will be auto-remove it after be expired
function timer:post(func, delay, opt)
    return self:post_at(func, os.mclock() + delay, delay, opt)
end

-- post timer task at the absolute time and will be auto-remove it after be expired
--
-- we can mark the returned task as canceled to cancel the pending task, e.g. task.cancel = true
--
function timer:post_at(func, when, period, opt)
    opt = opt or {}
    local task = {when = when, func = func, period = period, continuous = opt.continuous, cancel = false}
    self:_tasks():push(task)
    return task
end

-- post timer task after the relative time and will be auto-remove it after be expired
function timer:post_after(func, after, period, opt)
    return self:post_at(func, os.mclock() + after, period, opt)
end

-- get the delay of next task
function timer:delay()
    local delay = nil
    local tasks = self:_tasks()
    if tasks:length() > 0 then
        local task = tasks:peek()
        if task then
            local now = os.mclock()
            delay = task.when > now and task.when - now or 0
        end
    end
    return delay
end

-- run the timer next loop
function timer:next()
    local tasks = self:_tasks()
    while tasks:length() > 0 do
        local triggered = false
        local task = tasks:peek()
        if task then
            -- timeout or canceled?
            if task.cancel or task.when <= os.mclock() then
                tasks:pop()
                if task.continuous and not task.cancel then
                    task.when = os.mclock() + task.period
                    tasks:push(task)
                end
                -- run timer task
                if task.func then
                    local ok, errors = task.func(task.cancel)
                    if not ok then
                        return false, errors
                    end
                    triggered = true
                end
            end
        end
        if not triggered then
            break
        end
    end
    return true
end

-- kill all timer tasks
function timer:kill()
    local tasks = self:_tasks()
    while tasks:length() > 0 do
        local task = tasks:peek()
        if task then
            tasks:pop()
            if task.func then
                -- cancel it
                task.func(true)
            end
        end
    end
end

-- get timer name
function timer:name()
    return self._NAME
end

-- init timer
function timer:init(name)
    self._NAME  = name or "none"
    self._TASKS = heap.valueheap {cmp = function(a, b)
        return a.when < b.when
    end}
end

-- new timer
function timer:new(name)
    self = self()
    self:init(name)
    return self
end

-- return module: timer
return timer