-
Notifications
You must be signed in to change notification settings - Fork 76
/
ticker.py
125 lines (97 loc) · 3.03 KB
/
ticker.py
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
from twisted.internet.task import LoopingCall
class Task(object):
ticker = None
def stop(self):
self.ticker.remove(self)
class LoopTask(Task):
def __init__(self, ticker, interval, callback):
self.ticker = ticker
self.interval = interval
self.callback = callback
def update(self):
if self.ticker.tick % self.interval == 0:
self.callback()
class DelayTask(Task):
def __init__(self, ticker, delay, callback):
self.ticker = ticker
self.delay = delay
self.callback = callback
self.restart()
def restart(self):
self.target = self.ticker.tick + self.delay
def update(self):
if self.ticker.tick >= self.target:
self.callback()
self.stop()
class Ticker(object):
#: The current tick
tick = 0
#: Interval between ticks, in seconds
interval = 1.0/20
#: Maximum number of delayed ticks before they're all skipped
max_lag = 40
running = False
def __init__(self, logger):
self._logger = logger
self._tasks = []
self._impl = LoopingCall.withCount(self._update)
def start(self):
"""
Start running the tick loop.
"""
if not self.running:
self._impl.start(self.interval, now=False)
self.running = True
def stop(self):
"""
Stop running the tick loop.
"""
if self.running:
self._impl.stop()
self.running = False
def add_loop(self, interval, callback):
"""
Repeatedly run a callback.
:param interval: The interval in ticks
:param callback: The callback to run
:return: An instance providing a ``stop()`` method
"""
task = LoopTask(self, interval, self._wrap(callback))
self._tasks.append(task)
return task
def add_delay(self, delay, callback):
"""
Run a callback after a delay.
:param delay: The delay in ticks
:param callback: The callback to run
:return: An instance providing ``stop()`` and ``restart()`` methods
"""
task = DelayTask(self, delay, self._wrap(callback))
self._tasks.append(task)
return task
def remove(self, task):
"""
Removes a task, effectively cancelling it.
:param task: The task to remove
"""
self._tasks.remove(task)
def remove_all(self):
"""
Removes all registered tasks, effectively cancelling them.
"""
del self._tasks[:]
def _update(self, count):
if count >= self.max_lag:
self._logger.warn("Can't keep up! Skipping %d ticks" % (count - 1))
count = 1
for _ in range(count):
for task in list(self._tasks):
task.update()
self.tick += 1
def _wrap(self, callback):
def fn():
try:
callback()
except Exception as e:
self._logger.exception(e)
return fn