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