-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
gh-108277: Add wrapper for timerfd_create, timerfd_settime, and timerfd_gettime to os module #108382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-108277: Add wrapper for timerfd_create, timerfd_settime, and timerfd_gettime to os module #108382
Changes from 134 commits
0f8aaf5
208fd58
37f70cd
6f63cf7
0f47920
03f319d
b8e39f2
44b3ac7
a549684
5e33c3e
7da832d
d0905d9
0eed49a
fd718d9
4b89526
75614a1
458ae17
1f1d22e
dda4c31
909c50a
c53a84d
7e306f5
af8a9f2
74cdc6c
fc585b8
3879a71
04aa70b
efe3d1d
132ef60
c6d9fb0
f7af17c
94f248d
3366d58
d57cfbe
4b13a5c
94839ac
4c783e8
b65fd4c
bdbeef0
e1abd07
50e17c9
d8043a2
8024c9e
6e251af
914c7d0
cd6b32b
d411858
02dac34
a819904
f812cbe
6db493f
fcf3efc
44bc810
f742b8f
cbbc97b
977f574
f86afb2
0491421
de4477d
3915eea
4ae90da
e380aef
e7d967d
c7e8593
1d4d3b5
6565fea
f0623b9
64f1571
e8c8b60
74c12d1
8051b9b
e9d4753
ed6d3b2
ef0449a
1faf245
22315c8
cc63a06
ec8f2ec
1319814
7410a7c
aed85ab
1138cf9
95025c5
d0ed353
9309e4b
3548656
8a8b706
e1d993f
c2b8ddf
bd23f53
b97d694
c3f1904
3b8e3dc
2db35e3
180830f
ac7ee82
9cec110
86f5a8e
ce53a4f
814cc29
a48c3a6
da453b8
3f9bd62
69b9cfa
b2f507a
d5e782d
f83058a
49e6981
765e584
3d55e2e
0011278
6080d32
32ba03f
789b909
3a66967
817933e
c090ada
0e9f5b3
ae6b885
79b74b0
fa00db9
20a0674
908e3dc
171c71a
8fb3fdf
7c470fb
189843a
2741533
3d527da
2fd8804
5aba21f
dadcf60
6a77a1b
9d78861
24ae1d9
552638f
8fde913
f417c7c
4460475
ae8c3da
4aeb95e
f06e352
b8e3b23
ed34c2c
3fc0a5a
4783a13
7919b09
b2ad6c8
da47484
3661045
6d1e7c7
69897fc
5c78372
62782bc
4ff50a3
dc15134
92b7a32
f4b9865
e0f5d7c
6f1d788
64331c4
637c746
a7e501b
ed8a8c7
750110a
ccaf48d
b334a73
265ea56
945ef3c
6eefda5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3781,6 +3781,304 @@ features: | |
| .. versionadded:: 3.10 | ||
|
|
||
|
|
||
| Timer File Descriptors | ||
| ~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| .. versionadded:: 3.3 | ||
AA-Turner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| These functions provide support for Linux's *timer file descriptor* API. | ||
| Naturally, they are all only available on Linux. | ||
|
|
||
| The following example shows how to use a timer file descriptor | ||
| to execute a function twice a second: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| # Practical scripts should use really use a non-blocking timer, | ||
| # we use a blocking timer here for simplicity. | ||
| import os, time | ||
|
|
||
| # Create the timer file descriptor | ||
| fd = os.timerfd_create(time.CLOCK_REALTIME) | ||
vstinner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # Start the timer in 1 second, with an interval of half a second | ||
| os.timerfd_settime(fd, initial=1, interval=0.5) | ||
|
|
||
| try: | ||
| # Process timer events four times. | ||
| for _ in range(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 the :class:`float` type, | ||
| timer file descriptors allow specifying initial expiration and interval | ||
| in integer nanoseconds with ``_ns`` variants of the functions. | ||
|
|
||
| This example shows how :func:`~select.epoll` can be used with timer file | ||
| descriptors to wait until the file descriptor is ready for reading: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| import os, time, select, sys | ||
|
|
||
| # Create an epoll object | ||
| ep = select.epoll() | ||
|
|
||
| # Create timer file descriptors in non-blocking mode. | ||
| num = 3 | ||
| fds = [] | ||
| for _ in range(num): | ||
| fd = os.timerfd_create(time.CLOCK_REALTIME, os.TFD_NONBLOCK) | ||
| fds.append(fd) | ||
| # Register the timer file descriptor for read events | ||
| ep.register(fd, select.EPOLLIN) | ||
|
|
||
| # Start the timer with os.timerfd_settime_ns() in nanoseconds. | ||
| for i, fd in enumerate(fds, start=1): | ||
| i = i * 10**9 | ||
vstinner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| os.timerfd_settime_ns(fd, initial=i//4, interval=i//4) | ||
|
|
||
| timeout=3 | ||
| try: | ||
| # For simplicity, process a fixed number of timer events. | ||
| # Practical applications should use an infinite loop to process | ||
| # timer events, and use some trigger to break & stop the timer. | ||
| for _ in range(num*3): | ||
| # Wait for the timer to expire for 3 seconds. | ||
| events = ep.poll(timeout) | ||
| # XXX?: signaled events may be more than one at once. | ||
| print(f"Signaled events={events}") | ||
| for fd, event in events: | ||
| count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder) | ||
| print(f"Timer {fds.index(fd) + 1} expired {count} times") | ||
| finally: | ||
| for fd in fds: | ||
| ep.unregister(fd) | ||
| os.close(fd) | ||
| ep.close() | ||
|
|
||
| This example shows how :func:`~select.select` can be used with timer file | ||
| descriptors to wait until the file descriptor is ready for reading: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| import os, time, select, sys | ||
|
|
||
| # Create timer file descriptors in non-blocking mode. | ||
| num = 3 | ||
| fds = [os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK) | ||
| for _ in range(num)] | ||
|
|
||
| # Start the timers with os.timerfd_settime() in seconds. | ||
| # Timer 1 fires every 0.25 seconds; timer 2 every 0.5 seconds; etc | ||
| for i, fd in enumerate(fds, start=1): | ||
| os.timerfd_settime(fd, initial=i/4, interval=i/4) | ||
|
|
||
| timeout = 3 | ||
| try: | ||
| # For simplicity, process a fixed number of timer events. | ||
| # Practical applications should use an infinite loop to process | ||
| # timer events, and use some trigger to break & stop the timer. | ||
| for _ in range(num*3): | ||
| # Wait for the timer to expire for 3 seconds. | ||
| rfd, wfd, xfd = select.select(fds, fds, fds, timeout) | ||
| for fd in rfd: | ||
| count = int.from_bytes(os.read(fd, 8), byteorder=sys.byteorder) | ||
| print(f"Timer {fds.index(fd)+1} expired {count} times") | ||
| finally: | ||
| for fd in fds: | ||
| os.close(fd) | ||
|
|
||
| -------------- | ||
|
|
||
| .. function:: timerfd_create(clockid, /, *, flags=0) | ||
|
|
||
| Create and return a timer file descriptor (*timerfd*). | ||
|
|
||
| The file descriptor returned by :func:`timerfd_create` supports: | ||
|
||
|
|
||
| - :func:`read` | ||
| - :func:`~select.select` | ||
| - :func:`~select.poll`. | ||
|
|
||
| The file descriptor's :func:`read` method can be called with a buffer size of 8. | ||
| If the timer has already expired one or more times, :func:`read` returns | ||
| the number of expirations with the host's endianness, | ||
| which may be converted to an :class:`int` by ``int.from_bytes(x, byteorder=sys.byteorder)``. | ||
|
|
||
| :func:`~select.select` and :func:`~select.poll` can be used to wait until | ||
| timer expires and the file descriptor is readable. | ||
AA-Turner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| *clockid* must be a valid :ref:`clock ID <time-clock-id-constants>`, | ||
| as defined in the :py:mod:`time` module: | ||
|
|
||
| - :const:`time.CLOCK_REALTIME` | ||
| - :const:`time.CLOCK_MONOTONIC` | ||
| - :const:`time.CLOCK_BOOTTIME` (Since Linux 3.15) | ||
|
|
||
| If *clockid* is :const:`time.CLOCK_REALTIME`, a settable system-wide real-time clock | ||
| is used. If system clock is changed, timer setting need to be updated. | ||
| To cancel timer when system clock is changed, see :const:`TFD_TIMER_CANCEL_ON_SET`. | ||
|
|
||
| If *clockid* is :const:`time.CLOCK_MONOTONIC`, A nonsettable monotonically increasing | ||
| clock is used. Even if system clock is changed, timer setting will be not affected. | ||
|
|
||
| If *clockid* is :const:`time.CLOCK_BOOTTIME`, same as :const:`time.CLOCK_MONOTONIC` | ||
| except it includes any time that the system is suspended. | ||
|
|
||
| The file descriptor's behaviour can be modified by specifying a *flags* value. | ||
| Any of the following variables may used, combined using bitwise OR | ||
| (the ``|`` operator): | ||
|
|
||
| - :const:`TFD_NONBLOCK` | ||
| - :const:`TFD_CLOEXEC` | ||
|
|
||
| If :const:`TFD_NONBLOCK` is not set as a flag, :func:`read` blocks until the timer expires. | ||
|
||
| If it is set as a flag, :func:`read` doesn't block, but if there is no timer expiration, | ||
| :func:`read` raises :class:`OSError` with ``errno`` is set to :const:`errno.EAGAIN`. | ||
|
||
|
|
||
| If :const:`TFD_CLOEXEC` is set as a flag, set close-on-exec flag for new file descriptor. | ||
m-tmatma marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| The file descriptor must be closed with :func:`os.close` when it is no | ||
| longer needed, or else the file descriptor will be leaked. | ||
|
|
||
| .. seealso:: The :manpage:`timerfd_create(2)` man page. | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
|
|
||
| .. function:: timerfd_settime(fd, /, *, flags=flags, initial=0.0, interval=0.0) | ||
m-tmatma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Alter a timer file descriptor's internal timer. | ||
| This function operates the same interval timer as :func:`timerfd_settime_ns`. | ||
|
|
||
| *fd* must be a valid timer file descriptor. | ||
|
|
||
| The timer's behaviour can be modified by specifying a *flags* value. | ||
| Any of the following variables may used, combined using bitwise OR | ||
| (the ``|`` operator): | ||
|
|
||
| - :const:`TFD_TIMER_ABSTIME` | ||
| - :const:`TFD_TIMER_CANCEL_ON_SET` | ||
|
|
||
| The timer is disabled by setting *initial* to zero (``0``). | ||
| If *initial* is equal to or greater than zero, the timer is enabled. | ||
| If *initial* is less than zero, it raises an :class:`OSError` exception | ||
| with ``errno`` set to :const:`errno.EINVAL` | ||
|
|
||
| By default the timer will fire when *initial* seconds have elapsed. | ||
| (If *initial* is zero, timer will fire immediately.) | ||
|
|
||
| However, if the :const:`TFD_TIMER_ABSTIME` flag is set, | ||
| the timer will fire when the timer's clock | ||
| (set by *clockid* in :func:`timerfd_create`) reaches *initial* seconds. | ||
|
|
||
| The timer's interval is set by the *interval* :py:class:`float`. | ||
| If *interval* is zero, the timer only fires once, on the initial expiration. | ||
| If *interval* is greater than zero, the timer fires every time *interval* | ||
| seconds have elapsed since the previous expiration. | ||
| If *interval* is less than zero, it raises :class:`OSError` with ``errno`` | ||
| set to :const:`errno.EINVAL` | ||
|
|
||
| If the :const:`TFD_TIMER_CANCEL_ON_SET` flag is set along with :const:`TFD_TIMER_ABSTIME` | ||
| and the clock for this timer is :const:`time.CLOCK_REALTIME`, the timer is marked as | ||
| cancelable when if the real-time clock is changed discontinuously. Reading the descriptor | ||
| is aborted with with the error ECANCELED. | ||
|
|
||
| Linux manages system clock as UTC. A daylight-savings time transition is done | ||
| by changing time offset only and doesn't cause discontinuous system clock change. | ||
|
||
|
|
||
| Return a two-item tuple of (``next_expiration``, ``interval``) from | ||
| the previous timer state, before this function executed. | ||
|
|
||
| .. seealso:: The :manpage:`timerfd_settime(2)` man page. | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
|
|
||
| .. function:: timerfd_settime_ns(fd, /, *, flags=0, initial=0, interval=0) | ||
|
|
||
| Similar to :func:`timerfd_settime`, but use time as nanoseconds. | ||
| This function operates the same interval timer as :func:`timerfd_settime`. | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
|
|
||
| .. function:: timerfd_gettime(fd, /) | ||
|
|
||
| Return a two-item tuple of floats (``next_expiration``, ``interval``). | ||
|
|
||
| ``next_expiration`` denotes the relative time until next the timer next fires, | ||
| regardless of if the :const:`TFD_TIMER_ABSTIME` flag is set. | ||
|
|
||
| ``interval`` denotes the timer's interval. | ||
| If zero, the timer will only fire once, after ``next_expiration`` seconds have elapsed. | ||
|
|
||
| .. seealso:: :manpage:`timerfd_gettime(2)` | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
|
|
||
| .. function:: timerfd_gettime_ns(fd, /) | ||
|
|
||
| Similar to :func:`timerfd_gettime`, but return time as nanoseconds. | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
| .. data:: TFD_NONBLOCK | ||
AA-Turner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| A flag for the :func:`timerfd_create` function, | ||
| which sets the :const:`O_NONBLOCK` status flag for the new timer file descriptor. | ||
| If :const:`TFD_NONBLOCK` is not set as a flag, :func:`read` blocks. | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
| .. data:: TFD_CLOEXEC | ||
|
|
||
| A flag for the :func:`timerfd_create` function, | ||
| If :const:`TFD_CLOEXEC` is set as a flag, set close-on-exec flag for new file descriptor. | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
| .. data:: TFD_TIMER_ABSTIME | ||
|
|
||
| A flag for the :func:`timerfd_settime` and :func:`timerfd_settime_ns` functions. | ||
| If this flag is set, *initial* is interpreted as an absolute value on the timer's clock | ||
| (in UTC seconds or nanoseconds since the Unix Epoch). | ||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
| .. data:: TFD_TIMER_CANCEL_ON_SET | ||
|
|
||
| A flag for the :func:`timerfd_settime` and :func:`timerfd_settime_ns` functions | ||
| along with :const:`TFD_TIMER_ABSTIME`. | ||
| The timer is cancelled when the time of the underlying clock changes discontinuously. | ||
|
||
|
|
||
| .. availability:: Linux >= 2.6.27 with glibc >= 2.8 | ||
|
|
||
| .. versionadded:: 3.13 | ||
|
|
||
|
|
||
| Linux extended attributes | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AA-Turner
Thank you for pushing.