Skip to content

Commit 258e6e4

Browse files
committed
Timer can be scheduled for one-shot or repeating alarms. Can be given
offset and interval and then started/stopped.
1 parent afc0579 commit 258e6e4

2 files changed

Lines changed: 92 additions & 18 deletions

File tree

quick2wire/test_timerfd.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
from time import time
2+
from time import time, sleep
33
from contextlib import closing
44
from quick2wire.timerfd import Timer, timespec, itimerspec
55

@@ -9,18 +9,68 @@ def test_timespec_can_be_created_from_seconds():
99
assert t.tv_sec == 4
1010
assert t.tv_nsec == 125000000
1111

12+
1213
def test_itimerspec_can_be_created_from_seconds():
1314
t = itimerspec.from_seconds(offset=4.125, interval=1.25)
1415
assert t.it_value.tv_sec == 4
1516
assert t.it_value.tv_nsec == 125000000
1617
assert t.it_interval.tv_sec == 1
1718
assert t.it_interval.tv_nsec == 250000000
1819

20+
1921
def test_timer_waits_for_time_to_pass():
20-
with closing(Timer()) as timer:
22+
with closing(Timer(offset=0.125)) as timer:
2123
start = time()
22-
timer.schedule(0.125)
23-
n = timer.wait()
24+
25+
timer.start()
26+
timer.wait()
27+
28+
duration = time() - start
29+
30+
assert duration >= 0.125
31+
32+
33+
def test_timer_can_repeat_with_interval():
34+
with closing(Timer(interval=0.125)) as timer:
35+
start = time()
36+
37+
timer.start()
38+
timer.wait()
39+
timer.wait()
40+
2441
duration = time() - start
2542

26-
assert duration / n >= 0.125
43+
assert duration >= 0.25
44+
45+
46+
def test_timer_can_repeat_with_interval_after_offset():
47+
with closing(Timer(offset=0.25, interval=0.125)) as timer:
48+
start = time()
49+
50+
timer.start()
51+
timer.wait()
52+
timer.wait()
53+
timer.wait()
54+
55+
duration = time() - start
56+
57+
assert duration >= 0.5
58+
59+
60+
def test_timer_cannot_be_started_if_offset_and_interval_are_both_zero():
61+
with closing(Timer()) as timer:
62+
try:
63+
timer.start()
64+
assert False, "should have thrown ValueError"
65+
except ValueError:
66+
# expected
67+
pass
68+
69+
70+
def test_timer_reports_how_many_times_it_triggered_since_last_wait():
71+
with closing(Timer(interval=0.0125)) as timer:
72+
timer.start()
73+
sleep(0.5)
74+
n = timer.wait()
75+
76+
assert n >= 4

quick2wire/timerfd.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,25 +101,45 @@ def from_seconds(cls, offset, interval):
101101

102102

103103
class Timer:
104-
def __init__(self, clock=CLOCK_REALTIME, blocking=True):
104+
def __init__(self, clock=CLOCK_REALTIME, offset=0, interval=0, blocking=True):
105105
self._fd = timerfd_create(clock, (not blocking)*TFD_NONBLOCK)
106-
if self._fd < 0:
107-
e = get_errno()
108-
raise OSError(e, errno.strerror(e))
106+
self._offset = offset
107+
self._interval = interval
108+
self._started = False
109109

110110
def close(self):
111111
os.close(self._fd)
112112

113-
def schedule(self, offset=0, interval=0):
114-
spec = itimerspec(it_value=timespec.from_seconds(offset),
115-
it_interval=timespec.from_seconds(interval))
113+
@property
114+
def offset(self):
115+
return self._offset
116+
117+
@offset.setter
118+
def offset(self, new_offset):
119+
self._offset = new_offset
120+
if self._started:
121+
self._schedule()
122+
123+
@property
124+
def interval(self):
125+
return self._interval
126+
127+
@interval.setter
128+
def interval(self, new_interval):
129+
self._interval = new_interval
130+
if self._started:
131+
self._schedule()
132+
133+
def start(self):
134+
if self._offset == 0 and self._interval == 0:
135+
raise ValueError("timer will not fire because offset and interval are both zero")
116136

117-
print(spec)
137+
self._schedule(self._offset or self._interval, self._interval)
138+
self._started = True
118139

119-
status = timerfd_settime(self._fd, 0, byref(spec), None)
120-
if status < 0:
121-
e = get_errno()
122-
raise OSError(e, errno.strerror(e))
140+
def stop(self):
141+
self._schedule(0, 0)
142+
self._started = False
123143

124144
def wait(self):
125145
try:
@@ -130,4 +150,8 @@ def wait(self):
130150
return 0
131151
else:
132152
raise e
133-
153+
154+
def _schedule(self, offset, interval):
155+
spec = itimerspec.from_seconds(offset, interval)
156+
timerfd_settime(self._fd, 0, byref(spec), None)
157+

0 commit comments

Comments
 (0)