forked from romilly/quick2wire-python-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtimerfd.py
More file actions
213 lines (162 loc) · 6.75 KB
/
timerfd.py
File metadata and controls
213 lines (162 loc) · 6.75 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import math
import os
from ctypes import *
import struct
from contextlib import closing
import quick2wire.syscall as syscall
# From <time.h>
time_t = c_long
clockid_t = c_ulong
class timespec(Structure):
_fields_ = [("sec", time_t),
("nsec", c_long)]
__slots__ = [name for name,type in _fields_]
@classmethod
def from_seconds(cls, secs):
t = cls()
t.seconds = secs
return t
@property
def seconds(self):
if self.nsec == 0:
return self.sec
else:
return self.sec + self.nsec / 1000000000.0
@seconds.setter
def seconds(self, secs):
fractional, whole = math.modf(secs)
self.sec = int(whole)
self.nsec = int(fractional * 1000000000)
class itimerspec(Structure):
_fields_ = [("interval", timespec),
("value", timespec)]
__slots__ = [name for name,type in _fields_]
@classmethod
def from_seconds(cls, offset, interval):
spec = cls()
spec.value.seconds = offset
spec.interval.seconds = interval
return spec
# from <bits/time.h>
CLOCK_REALTIME = 0 # Identifier for system-wide realtime clock.
CLOCK_MONOTONIC = 1 # Monotonic system-wide clock.
CLOCK_PROCESS_CPUTIME_ID = 2 # High-resolution timer from the CPU
CLOCK_THREAD_CPUTIME_ID = 3 # Thread-specific CPU-time clock.
CLOCK_MONOTONIC_RAW = 4 # Monotonic system-wide clock, not adjusted for frequency scaling.
CLOCK_REALTIME_COARSE = 5 # Identifier for system-wide realtime clock, updated only on ticks.
CLOCK_MONOTONIC_COARSE = 6 # Monotonic system-wide clock, updated only on ticks.
CLOCK_BOOTTIME = 7 # Monotonic system-wide clock that includes time spent in suspension.
CLOCK_REALTIME_ALARM = 8 # Like CLOCK_REALTIME but also wakes suspended system.
CLOCK_BOOTTIME_ALARM = 9 # Like CLOCK_BOOTTIME but also wakes suspended system.
# From <sys/timerfd.h>
# Bits to be set in the FLAGS parameter of `timerfd_create'.
TFD_CLOEXEC = 0o2000000,
TFD_NONBLOCK = 0o4000
# Bits to be set in the FLAGS parameter of `timerfd_settime'.
TFD_TIMER_ABSTIME = 1 << 0
# Return file descriptor for new interval timer source.
#
# extern int timerfd_create (clockid_t __clock_id, int __flags)
timerfd_create = syscall.lookup(c_int, "timerfd_create", (clockid_t, c_int))
# Set next expiration time of interval timer source UFD to UTMR. If
# FLAGS has the TFD_TIMER_ABSTIME flag set the timeout value is
# absolute. Optionally return the old expiration time in OTMR.
#
# extern int timerfd_settime (int __ufd, int __flags,
# __const struct itimerspec *__utmr,
# struct itimerspec *__otmr)
timerfd_settime = syscall.lookup(c_int, "timerfd_settime", (c_int, c_int, POINTER(itimerspec), POINTER(itimerspec)))
# Return the next expiration time of UFD.
#
# extern int timerfd_gettime (int __ufd, struct itimerspec *__otmr)
timerfd_gettime = syscall.lookup(c_int, "timerfd_gettime", (c_int, POINTER(itimerspec)))
class Timer(syscall.SelfClosing):
"""A one-shot or repeating timer that can be added to a Selector."""
def __init__(self, offset=0, interval=0, blocking=True, clock=CLOCK_REALTIME):
"""Creates a new Timer.
Arguments:
offset -- the initial expiration time, measured in seconds from
the call to start().
interval -- if non-zero, the interval for periodic timer expirations,
measured in seconds.
blocking -- if False calls to wait() do not block until the timer
expires but return 0 if the timer has not expired.
(default = True)
clock -- the system clock used to measure time:
CLOCK_REALTIME -- system-wide realtime clock.
CLOCK_MONOTONIC -- monotonic system-wide clock.
"""
self._clock = clock
self._flags = (not blocking)*TFD_NONBLOCK
self._fd = None
self._offset = offset
self._interval = interval
self._started = False
def close(self):
"""Closes the Timer and releases its file descriptor."""
if self._fd is not None:
os.close(self._fd)
self._fd = None
def fileno(self):
"""Returns the Timer's file descriptor."""
if self._fd is None:
self._fd = timerfd_create(self._clock, self._flags)
return self._fd
@property
def offset(self):
"""the initial expiration time, measured in seconds from the call to start()."""
return self._offset
@offset.setter
def offset(self, new_offset):
self._offset = new_offset
if self._started:
self._apply_schedule()
@property
def interval(self):
"""The interval, specified in seconds, with which the timer will repeat.
If zero, the timer only fires once, when the offset expires.
"""
return self._interval
@interval.setter
def interval(self, new_interval):
self._interval = new_interval
if self._started:
self._apply_schedule()
def start(self):
"""Starts the timer running.
Raises:
ValueError -- if offset and interval are both zero.
"""
if self._offset == 0 and self._interval == 0:
raise ValueError("timer will not fire because offset and interval are both zero")
self._apply_schedule()
self._started = True
def stop(self):
"""Stops the timer running. Any scheduled timer events will not fire."""
self._schedule(0, 0)
self._started = False
def wait(self):
"""Receives timer events.
If the timer has already expired one or more times since its
settings were last modified or wait() was last called then
wait() returns the number of expirations that have occurred.
If no timer expirations have occurred, then the call either
blocks until the next timer expiration, or returns 0 if the
Timer is non-blocking (was created with the blocking parameter
set to False).
Raises:
OSError -- an OS error occurred reading the state of the timer.
"""
try:
buf = os.read(self.fileno(), 8)
return struct.unpack("Q", buf)[0]
except OSError as e:
if e.errno == errno.EAGAIN:
return 0
else:
raise e
def _apply_schedule(self):
self._schedule(self._offset or self._interval, self._interval)
def _schedule(self, offset, interval):
spec = itimerspec.from_seconds(offset, interval)
timerfd_settime(self.fileno(), 0, byref(spec), None)