timer file descriptor HOWTO¶
- Release:
1.13
This HOWTO discusses Python’s support for the linux timer file descriptor.
Examples¶
The following example shows how to use a timer file descriptorto execute a function twice a second:
# Practical scripts should use really use a non-blocking timer,# we use a blocking timer here for simplicity.importos,time# Create the timer file descriptorfd=os.timerfd_create(time.CLOCK_REALTIME)# Start the timer in 1 second, with an interval of half a secondos.timerfd_settime(fd,initial=1,interval=0.5)try:# Process timer events four times.for_inrange(4):# read() will block until the timer expires_=os.read(fd,8)print("Timer expired")finally:# Remember to close the timer file descriptor!os.close(fd)
To avoid the precision loss caused by thefloat
type,timer file descriptors allow specifying initial expiration and intervalin integer nanoseconds with_ns
variants of the functions.
This example shows howepoll()
can be used with timer filedescriptors to wait until the file descriptor is ready for reading:
importos,time,select,socket,sys# Create an epoll objectep=select.epoll()# In this example, use loopback address to send "stop" command to the server.## $ telnet 127.0.0.1 1234# Trying 127.0.0.1...# Connected to 127.0.0.1.# Escape character is '^]'.# stop# Connection closed by foreign host.#sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.bind(("127.0.0.1",1234))sock.setblocking(False)sock.listen(1)ep.register(sock,select.EPOLLIN)# Create timer file descriptors in non-blocking mode.num=3fds=[]for_inrange(num):fd=os.timerfd_create(time.CLOCK_REALTIME,flags=os.TFD_NONBLOCK)fds.append(fd)# Register the timer file descriptor for read eventsep.register(fd,select.EPOLLIN)# Start the timer with os.timerfd_settime_ns() in nanoseconds.# Timer 1 fires every 0.25 seconds; timer 2 every 0.5 seconds; etcfori,fdinenumerate(fds,start=1):one_sec_in_nsec=10**9i=i*one_sec_in_nsecos.timerfd_settime_ns(fd,initial=i//4,interval=i//4)timeout=3try:conn=Noneis_active=Truewhileis_active:# Wait for the timer to expire for 3 seconds.# epoll.poll() returns a list of (fd, event) pairs.# fd is a file descriptor.# sock and conn[=returned value of socket.accept()] are socket objects, not file descriptors.# So use sock.fileno() and conn.fileno() to get the file descriptors.events=ep.poll(timeout)# If more than one timer file descriptors are ready for reading at once,# epoll.poll() returns a list of (fd, event) pairs.## In this example settings,# 1st timer fires every 0.25 seconds in 0.25 seconds. (0.25, 0.5, 0.75, 1.0, ...)# 2nd timer every 0.5 seconds in 0.5 seconds. (0.5, 1.0, 1.5, 2.0, ...)# 3rd timer every 0.75 seconds in 0.75 seconds. (0.75, 1.5, 2.25, 3.0, ...)## In 0.25 seconds, only 1st timer fires.# In 0.5 seconds, 1st timer and 2nd timer fires at once.# In 0.75 seconds, 1st timer and 3rd timer fires at once.# In 1.5 seconds, 1st timer, 2nd timer and 3rd timer fires at once.## If a timer file descriptor is signaled more than once since# the last os.read() call, os.read() returns the number of signaled# as host order of class bytes.print(f"Signaled events={events}")forfd,eventinevents:ifevent&select.EPOLLIN:iffd==sock.fileno():# Check if there is a connection request.print(f"Accepting connection{fd}")conn,addr=sock.accept()conn.setblocking(False)print(f"Accepted connection{conn} from{addr}")ep.register(conn,select.EPOLLIN)elifconnandfd==conn.fileno():# Check if there is data to read.print(f"Reading data{fd}")data=conn.recv(1024)ifdata:# You should catch UnicodeDecodeError exception for safety.cmd=data.decode()ifcmd.startswith("stop"):print(f"Stopping server")is_active=Falseelse:print(f"Unknown command:{cmd}")else:# No more data, close connectionprint(f"Closing connection{fd}")ep.unregister(conn)conn.close()conn=Noneeliffdinfds:print(f"Reading timer{fd}")count=int.from_bytes(os.read(fd,8),byteorder=sys.byteorder)print(f"Timer{fds.index(fd)+1} expired{count} times")else:print(f"Unknown file descriptor{fd}")finally:forfdinfds:ep.unregister(fd)os.close(fd)ep.close()
This example shows howselect()
can be used with timer filedescriptors to wait until the file descriptor is ready for reading:
importos,time,select,socket,sys# In this example, use loopback address to send "stop" command to the server.## $ telnet 127.0.0.1 1234# Trying 127.0.0.1...# Connected to 127.0.0.1.# Escape character is '^]'.# stop# Connection closed by foreign host.#sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.bind(("127.0.0.1",1234))sock.setblocking(False)sock.listen(1)# Create timer file descriptors in non-blocking mode.num=3fds=[os.timerfd_create(time.CLOCK_REALTIME,flags=os.TFD_NONBLOCK)for_inrange(num)]select_fds=fds+[sock]# Start the timers with os.timerfd_settime() in seconds.# Timer 1 fires every 0.25 seconds; timer 2 every 0.5 seconds; etcfori,fdinenumerate(fds,start=1):os.timerfd_settime(fd,initial=i/4,interval=i/4)timeout=3try:conn=Noneis_active=Truewhileis_active:# Wait for the timer to expire for 3 seconds.# select.select() returns a list of file descriptors or objects.rfd,wfd,xfd=select.select(select_fds,select_fds,select_fds,timeout)forfdinrfd:iffd==sock:# Check if there is a connection request.print(f"Accepting connection{fd}")conn,addr=sock.accept()conn.setblocking(False)print(f"Accepted connection{conn} from{addr}")select_fds.append(conn)elifconnandfd==conn:# Check if there is data to read.print(f"Reading data{fd}")data=conn.recv(1024)ifdata:# You should catch UnicodeDecodeError exception for safety.cmd=data.decode()ifcmd.startswith("stop"):print(f"Stopping server")is_active=Falseelse:print(f"Unknown command:{cmd}")else:# No more data, close connectionprint(f"Closing connection{fd}")select_fds.remove(conn)conn.close()conn=Noneeliffdinfds:print(f"Reading timer{fd}")count=int.from_bytes(os.read(fd,8),byteorder=sys.byteorder)print(f"Timer{fds.index(fd)+1} expired{count} times")else:print(f"Unknown file descriptor{fd}")finally:forfdinfds:os.close(fd)sock.close()sock=None