• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1.. _timerfd-howto:
2
3*****************************
4  timer file descriptor HOWTO
5*****************************
6
7:Release: 1.13
8
9This HOWTO discusses Python's support for the linux timer file descriptor.
10
11
12Examples
13========
14
15The following example shows how to use a timer file descriptor
16to execute a function twice a second:
17
18.. code-block:: python
19
20   # Practical scripts should use really use a non-blocking timer,
21   # we use a blocking timer here for simplicity.
22   import os, time
23
24   # Create the timer file descriptor
25   fd = os.timerfd_create(time.CLOCK_REALTIME)
26
27   # Start the timer in 1 second, with an interval of half a second
28   os.timerfd_settime(fd, initial=1, interval=0.5)
29
30   try:
31       # Process timer events four times.
32       for _ in range(4):
33           # read() will block until the timer expires
34           _ = os.read(fd, 8)
35           print("Timer expired")
36   finally:
37       # Remember to close the timer file descriptor!
38       os.close(fd)
39
40To avoid the precision loss caused by the :class:`float` type,
41timer file descriptors allow specifying initial expiration and interval
42in integer nanoseconds with ``_ns`` variants of the functions.
43
44This example shows how :func:`~select.epoll` can be used with timer file
45descriptors to wait until the file descriptor is ready for reading:
46
47.. code-block:: python
48
49   import os, time, select, socket, sys
50
51   # Create an epoll object
52   ep = select.epoll()
53
54   # In this example, use loopback address to send "stop" command to the server.
55   #
56   # $ telnet 127.0.0.1 1234
57   # Trying 127.0.0.1...
58   # Connected to 127.0.0.1.
59   # Escape character is '^]'.
60   # stop
61   # Connection closed by foreign host.
62   #
63   sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
64   sock.bind(("127.0.0.1", 1234))
65   sock.setblocking(False)
66   sock.listen(1)
67   ep.register(sock, select.EPOLLIN)
68
69   # Create timer file descriptors in non-blocking mode.
70   num = 3
71   fds = []
72   for _ in range(num):
73       fd = os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
74       fds.append(fd)
75       # Register the timer file descriptor for read events
76       ep.register(fd, select.EPOLLIN)
77
78   # Start the timer with os.timerfd_settime_ns() in nanoseconds.
79   # Timer 1 fires every 0.25 seconds; timer 2 every 0.5 seconds; etc
80   for i, fd in enumerate(fds, start=1):
81       one_sec_in_nsec = 10**9
82       i = i * one_sec_in_nsec
83       os.timerfd_settime_ns(fd, initial=i//4, interval=i//4)
84
85   timeout = 3
86   try:
87       conn = None
88       is_active = True
89       while is_active:
90           # Wait for the timer to expire for 3 seconds.
91           # epoll.poll() returns a list of (fd, event) pairs.
92           # fd is a file descriptor.
93           # sock and conn[=returned value of socket.accept()] are socket objects, not file descriptors.
94           # So use sock.fileno() and conn.fileno() to get the file descriptors.
95           events = ep.poll(timeout)
96
97           # If more than one timer file descriptors are ready for reading at once,
98           # epoll.poll() returns a list of (fd, event) pairs.
99           #
100           # In this example settings,
101           #    1st timer fires every 0.25 seconds in 0.25 seconds. (0.25, 0.5, 0.75, 1.0, ...)
102           #    2nd timer every 0.5 seconds in 0.5 seconds. (0.5, 1.0, 1.5, 2.0, ...)
103           #    3rd timer every 0.75 seconds in 0.75 seconds. (0.75, 1.5, 2.25, 3.0, ...)
104           #
105           #    In 0.25 seconds, only 1st timer fires.
106           #    In 0.5 seconds, 1st timer and 2nd timer fires at once.
107           #    In 0.75 seconds, 1st timer and 3rd timer fires at once.
108           #    In 1.5 seconds, 1st timer, 2nd timer and 3rd timer fires at once.
109           #
110           # If a timer file descriptor is signaled more than once since
111           # the last os.read() call, os.read() returns the number of signaled
112           # as host order of class bytes.
113           print(f"Signaled events={events}")
114           for fd, event in events:
115               if event & select.EPOLLIN:
116                   if fd == sock.fileno():
117                       # Check if there is a connection request.
118                       print(f"Accepting connection {fd}")
119                       conn, addr = sock.accept()
120                       conn.setblocking(False)
121                       print(f"Accepted connection {conn} from {addr}")
122                       ep.register(conn, select.EPOLLIN)
123                   elif conn and fd == conn.fileno():
124                       # Check if there is data to read.
125                       print(f"Reading data {fd}")
126                       data = conn.recv(1024)
127                       if data:
128                           # You should catch UnicodeDecodeError exception for safety.
129                           cmd = data.decode()
130                           if cmd.startswith("stop"):
131                               print(f"Stopping server")
132                               is_active = False
133                           else:
134                               print(f"Unknown command: {cmd}")
135                       else:
136                           # No more data, close connection
137                           print(f"Closing connection {fd}")
138                           ep.unregister(conn)
139                           conn.close()
140                           conn = None
141                   elif fd in fds:
142                       print(f"Reading timer {fd}")
143                       count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder)
144                       print(f"Timer {fds.index(fd) + 1} expired {count} times")
145                   else:
146                       print(f"Unknown file descriptor {fd}")
147   finally:
148       for fd in fds:
149           ep.unregister(fd)
150           os.close(fd)
151       ep.close()
152
153This example shows how :func:`~select.select` can be used with timer file
154descriptors to wait until the file descriptor is ready for reading:
155
156.. code-block:: python
157
158   import os, time, select, socket, sys
159
160   # In this example, use loopback address to send "stop" command to the server.
161   #
162   # $ telnet 127.0.0.1 1234
163   # Trying 127.0.0.1...
164   # Connected to 127.0.0.1.
165   # Escape character is '^]'.
166   # stop
167   # Connection closed by foreign host.
168   #
169   sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
170   sock.bind(("127.0.0.1", 1234))
171   sock.setblocking(False)
172   sock.listen(1)
173
174   # Create timer file descriptors in non-blocking mode.
175   num = 3
176   fds = [os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
177          for _ in range(num)]
178   select_fds = fds + [sock]
179
180   # Start the timers with os.timerfd_settime() in seconds.
181   # Timer 1 fires every 0.25 seconds; timer 2 every 0.5 seconds; etc
182   for i, fd in enumerate(fds, start=1):
183      os.timerfd_settime(fd, initial=i/4, interval=i/4)
184
185   timeout = 3
186   try:
187       conn = None
188       is_active = True
189       while is_active:
190          # Wait for the timer to expire for 3 seconds.
191          # select.select() returns a list of file descriptors or objects.
192          rfd, wfd, xfd = select.select(select_fds, select_fds, select_fds, timeout)
193          for fd in rfd:
194              if fd == sock:
195                  # Check if there is a connection request.
196                  print(f"Accepting connection {fd}")
197                  conn, addr = sock.accept()
198                  conn.setblocking(False)
199                  print(f"Accepted connection {conn} from {addr}")
200                  select_fds.append(conn)
201              elif conn and fd == conn:
202                  # Check if there is data to read.
203                  print(f"Reading data {fd}")
204                  data = conn.recv(1024)
205                  if data:
206                      # You should catch UnicodeDecodeError exception for safety.
207                      cmd = data.decode()
208                      if cmd.startswith("stop"):
209                          print(f"Stopping server")
210                          is_active = False
211                      else:
212                          print(f"Unknown command: {cmd}")
213                  else:
214                      # No more data, close connection
215                      print(f"Closing connection {fd}")
216                      select_fds.remove(conn)
217                      conn.close()
218                      conn = None
219              elif fd in fds:
220                  print(f"Reading timer {fd}")
221                  count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder)
222                  print(f"Timer {fds.index(fd) + 1} expired {count} times")
223              else:
224                  print(f"Unknown file descriptor {fd}")
225   finally:
226       for fd in fds:
227          os.close(fd)
228       sock.close()
229       sock = None
230
231