mirror of https://github.com/python/cpython
gh-108277: Add os.timerfd_create() function (#108382)
Add wrapper for timerfd_create, timerfd_settime, and timerfd_gettime to os module. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
64f158e7b0
commit
de2a4036cb
|
@ -33,4 +33,5 @@ Currently, the HOWTOs are:
|
||||||
perf_profiling.rst
|
perf_profiling.rst
|
||||||
annotations.rst
|
annotations.rst
|
||||||
isolating-extensions.rst
|
isolating-extensions.rst
|
||||||
|
timerfd.rst
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
.. _timerfd-howto:
|
||||||
|
|
||||||
|
*****************************
|
||||||
|
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 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)
|
||||||
|
|
||||||
|
# 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, socket, sys
|
||||||
|
|
||||||
|
# Create an epoll object
|
||||||
|
ep = 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 = 3
|
||||||
|
fds = []
|
||||||
|
for _ in range(num):
|
||||||
|
fd = os.timerfd_create(time.CLOCK_REALTIME, flags=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.
|
||||||
|
# Timer 1 fires every 0.25 seconds; timer 2 every 0.5 seconds; etc
|
||||||
|
for i, fd in enumerate(fds, start=1):
|
||||||
|
one_sec_in_nsec = 10**9
|
||||||
|
i = i * one_sec_in_nsec
|
||||||
|
os.timerfd_settime_ns(fd, initial=i//4, interval=i//4)
|
||||||
|
|
||||||
|
timeout = 3
|
||||||
|
try:
|
||||||
|
conn = None
|
||||||
|
is_active = True
|
||||||
|
while is_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 nubmer of signaled
|
||||||
|
# as host order of class bytes.
|
||||||
|
print(f"Signaled events={events}")
|
||||||
|
for fd, event in events:
|
||||||
|
if event & select.EPOLLIN:
|
||||||
|
if fd == 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)
|
||||||
|
elif conn and fd == conn.fileno():
|
||||||
|
# Check if there is data to read.
|
||||||
|
print(f"Reading data {fd}")
|
||||||
|
data = conn.recv(1024)
|
||||||
|
if data:
|
||||||
|
# You should catch UnicodeDecodeError exception for safety.
|
||||||
|
cmd = data.decode()
|
||||||
|
if cmd.startswith("stop"):
|
||||||
|
print(f"Stopping server")
|
||||||
|
is_active = False
|
||||||
|
else:
|
||||||
|
print(f"Unknown command: {cmd}")
|
||||||
|
else:
|
||||||
|
# No more data, close connection
|
||||||
|
print(f"Closing connection {fd}")
|
||||||
|
ep.unregister(conn)
|
||||||
|
conn.close()
|
||||||
|
conn = None
|
||||||
|
elif fd in fds:
|
||||||
|
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:
|
||||||
|
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, 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 = 3
|
||||||
|
fds = [os.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
|
||||||
|
for _ in range(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; etc
|
||||||
|
for i, fd in enumerate(fds, start=1):
|
||||||
|
os.timerfd_settime(fd, initial=i/4, interval=i/4)
|
||||||
|
|
||||||
|
timeout = 3
|
||||||
|
try:
|
||||||
|
conn = None
|
||||||
|
is_active = True
|
||||||
|
while is_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)
|
||||||
|
for fd in rfd:
|
||||||
|
if fd == 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)
|
||||||
|
elif conn and fd == conn:
|
||||||
|
# Check if there is data to read.
|
||||||
|
print(f"Reading data {fd}")
|
||||||
|
data = conn.recv(1024)
|
||||||
|
if data:
|
||||||
|
# You should catch UnicodeDecodeError exception for safety.
|
||||||
|
cmd = data.decode()
|
||||||
|
if cmd.startswith("stop"):
|
||||||
|
print(f"Stopping server")
|
||||||
|
is_active = False
|
||||||
|
else:
|
||||||
|
print(f"Unknown command: {cmd}")
|
||||||
|
else:
|
||||||
|
# No more data, close connection
|
||||||
|
print(f"Closing connection {fd}")
|
||||||
|
select_fds.remove(conn)
|
||||||
|
conn.close()
|
||||||
|
conn = None
|
||||||
|
elif fd in fds:
|
||||||
|
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:
|
||||||
|
for fd in fds:
|
||||||
|
os.close(fd)
|
||||||
|
sock.close()
|
||||||
|
sock = None
|
||||||
|
|
|
@ -3781,6 +3781,217 @@ features:
|
||||||
.. versionadded:: 3.10
|
.. versionadded:: 3.10
|
||||||
|
|
||||||
|
|
||||||
|
Timer File Descriptors
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
These functions provide support for Linux's *timer file descriptor* API.
|
||||||
|
Naturally, they are all only available on Linux.
|
||||||
|
|
||||||
|
.. 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.
|
||||||
|
|
||||||
|
*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 for timerfd_create)
|
||||||
|
|
||||||
|
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 non-settable monotonically
|
||||||
|
increasing clock is used. Even if the system clock is changed, the timer
|
||||||
|
setting will not be 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 hasn't been an expiration since the last call to read,
|
||||||
|
:func:`read` raises :class:`OSError` with ``errno`` is set to
|
||||||
|
:const:`errno.EAGAIN`.
|
||||||
|
|
||||||
|
:const:`TFD_CLOEXEC` is always set by Python automatically.
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 if the
|
||||||
|
real-time clock is changed discontinuously. Reading the descriptor is
|
||||||
|
aborted 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.
|
||||||
|
|
||||||
|
Discontinuous system clock change will be caused by the following events:
|
||||||
|
|
||||||
|
- ``settimeofday``
|
||||||
|
- ``clock_settime``
|
||||||
|
- set the system date and time by ``date`` command
|
||||||
|
|
||||||
|
Return a two-item tuple of (``next_expiration``, ``interval``) from
|
||||||
|
the previous timer state, before this function executed.
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
:manpage:`timerfd_create(2)`, :manpage:`timerfd_settime(2)`,
|
||||||
|
:manpage:`settimeofday(2)`, :manpage:`clock_settime(2)`,
|
||||||
|
and :manpage:`date(1)`.
|
||||||
|
|
||||||
|
.. 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
|
||||||
|
|
||||||
|
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
|
Linux extended attributes
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,14 @@ os
|
||||||
usable by the calling thread of the current process.
|
usable by the calling thread of the current process.
|
||||||
(Contributed by Victor Stinner in :gh:`109649`.)
|
(Contributed by Victor Stinner in :gh:`109649`.)
|
||||||
|
|
||||||
|
* Add a low level interface for Linux's timer notification file descriptors
|
||||||
|
via :func:`os.timerfd_create`,
|
||||||
|
:func:`os.timerfd_settime`, :func:`os.timerfd_settime_ns`,
|
||||||
|
:func:`os.timerfd_gettime`, and :func:`os.timerfd_gettime_ns`,
|
||||||
|
:const:`os.TFD_NONBLOCK`, :const:`os.TFD_CLOEXEC`,
|
||||||
|
:const:`os.TFD_TIMER_ABSTIME`, and :const:`os.TFD_TIMER_CANCEL_ON_SET`
|
||||||
|
(Contributed by Masaru Tsuchiyama in :gh:`108277`.)
|
||||||
|
|
||||||
pathlib
|
pathlib
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -993,6 +993,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(instructions));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(instructions));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(intern));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(intern));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(intersection));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(intersection));
|
||||||
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(interval));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(is_running));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(is_running));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(isatty));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(isatty));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(isinstance));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(isinstance));
|
||||||
|
|
|
@ -482,6 +482,7 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(instructions)
|
STRUCT_FOR_ID(instructions)
|
||||||
STRUCT_FOR_ID(intern)
|
STRUCT_FOR_ID(intern)
|
||||||
STRUCT_FOR_ID(intersection)
|
STRUCT_FOR_ID(intersection)
|
||||||
|
STRUCT_FOR_ID(interval)
|
||||||
STRUCT_FOR_ID(is_running)
|
STRUCT_FOR_ID(is_running)
|
||||||
STRUCT_FOR_ID(isatty)
|
STRUCT_FOR_ID(isatty)
|
||||||
STRUCT_FOR_ID(isinstance)
|
STRUCT_FOR_ID(isinstance)
|
||||||
|
|
|
@ -991,6 +991,7 @@ extern "C" {
|
||||||
INIT_ID(instructions), \
|
INIT_ID(instructions), \
|
||||||
INIT_ID(intern), \
|
INIT_ID(intern), \
|
||||||
INIT_ID(intersection), \
|
INIT_ID(intersection), \
|
||||||
|
INIT_ID(interval), \
|
||||||
INIT_ID(is_running), \
|
INIT_ID(is_running), \
|
||||||
INIT_ID(isatty), \
|
INIT_ID(isatty), \
|
||||||
INIT_ID(isinstance), \
|
INIT_ID(isinstance), \
|
||||||
|
|
|
@ -143,6 +143,10 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
|
||||||
// Export for '_socket' shared extension.
|
// Export for '_socket' shared extension.
|
||||||
PyAPI_FUNC(_PyTime_t) _PyTime_FromSeconds(int seconds);
|
PyAPI_FUNC(_PyTime_t) _PyTime_FromSeconds(int seconds);
|
||||||
|
|
||||||
|
// Create a timestamp from a number of seconds in double.
|
||||||
|
// Export for '_socket' shared extension.
|
||||||
|
PyAPI_FUNC(_PyTime_t) _PyTime_FromSecondsDouble(double seconds, _PyTime_round_t round);
|
||||||
|
|
||||||
// Macro to create a timestamp from a number of seconds, no integer overflow.
|
// Macro to create a timestamp from a number of seconds, no integer overflow.
|
||||||
// Only use the macro for small values, prefer _PyTime_FromSeconds().
|
// Only use the macro for small values, prefer _PyTime_FromSeconds().
|
||||||
#define _PYTIME_FROMSECONDS(seconds) \
|
#define _PYTIME_FROMSECONDS(seconds) \
|
||||||
|
@ -241,7 +245,7 @@ PyAPI_FUNC(int) _PyTime_AsTimevalTime_t(
|
||||||
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE)
|
#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE)
|
||||||
// Create a timestamp from a timespec structure.
|
// Create a timestamp from a timespec structure.
|
||||||
// Raise an exception and return -1 on overflow, return 0 on success.
|
// Raise an exception and return -1 on overflow, return 0 on success.
|
||||||
extern int _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts);
|
extern int _PyTime_FromTimespec(_PyTime_t *tp, const struct timespec *ts);
|
||||||
|
|
||||||
// Convert a timestamp to a timespec structure (nanosecond resolution).
|
// Convert a timestamp to a timespec structure (nanosecond resolution).
|
||||||
// tv_nsec is always positive.
|
// tv_nsec is always positive.
|
||||||
|
|
|
@ -1287,6 +1287,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(intersection);
|
string = &_Py_ID(intersection);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
string = &_Py_ID(interval);
|
||||||
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(is_running);
|
string = &_Py_ID(is_running);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
|
|
@ -3926,6 +3926,356 @@ class EventfdTests(unittest.TestCase):
|
||||||
self.assertEqual((rfd, wfd, xfd), ([fd], [], []))
|
self.assertEqual((rfd, wfd, xfd), ([fd], [], []))
|
||||||
os.eventfd_read(fd)
|
os.eventfd_read(fd)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(os, 'timerfd_create'), 'requires os.timerfd_create')
|
||||||
|
@support.requires_linux_version(2, 6, 30)
|
||||||
|
class TimerfdTests(unittest.TestCase):
|
||||||
|
def timerfd_create(self, *args, **kwargs):
|
||||||
|
fd = os.timerfd_create(*args, **kwargs)
|
||||||
|
self.assertGreaterEqual(fd, 0)
|
||||||
|
self.assertFalse(os.get_inheritable(fd))
|
||||||
|
self.addCleanup(os.close, fd)
|
||||||
|
return fd
|
||||||
|
|
||||||
|
def test_timerfd_initval(self):
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
initial_expiration = 0.25
|
||||||
|
interval = 0.125
|
||||||
|
|
||||||
|
# 1st call
|
||||||
|
next_expiration, interval2 = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
|
||||||
|
self.assertAlmostEqual(interval2, 0.0, places=3)
|
||||||
|
self.assertAlmostEqual(next_expiration, 0.0, places=3)
|
||||||
|
|
||||||
|
# 2nd call
|
||||||
|
next_expiration, interval2 = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
|
||||||
|
self.assertAlmostEqual(interval2, interval, places=3)
|
||||||
|
self.assertAlmostEqual(next_expiration, initial_expiration, places=3)
|
||||||
|
|
||||||
|
# timerfd_gettime
|
||||||
|
next_expiration, interval2 = os.timerfd_gettime(fd)
|
||||||
|
self.assertAlmostEqual(interval2, interval, places=3)
|
||||||
|
self.assertAlmostEqual(next_expiration, initial_expiration, places=3)
|
||||||
|
|
||||||
|
def test_timerfd_non_blocking(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
|
||||||
|
|
||||||
|
# 0.1 second later
|
||||||
|
initial_expiration = 0.1
|
||||||
|
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=0)
|
||||||
|
|
||||||
|
# read() raises OSError with errno is EAGAIN for non-blocking timer.
|
||||||
|
with self.assertRaises(OSError) as ctx:
|
||||||
|
_ = os.read(fd, size)
|
||||||
|
self.assertEqual(ctx.exception.errno, errno.EAGAIN)
|
||||||
|
|
||||||
|
# Wait more than 0.1 seconds
|
||||||
|
time.sleep(initial_expiration + 0.1)
|
||||||
|
|
||||||
|
# confirm if timerfd is readable and read() returns 1 as bytes.
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
|
||||||
|
def test_timerfd_negative(self):
|
||||||
|
one_sec_in_nsec = 10**9
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
# Any of 'initial' and 'interval' is negative value.
|
||||||
|
for initial, interval in ( (-1, 0), (1, -1), (-1, -1), (-0.1, 0), (1, -0.1), (-0.1, -0.1)):
|
||||||
|
for flags in (0, os.TFD_TIMER_ABSTIME, os.TFD_TIMER_ABSTIME|os.TFD_TIMER_CANCEL_ON_SET):
|
||||||
|
with self.subTest(flags=flags, initial=initial, interval=interval):
|
||||||
|
with self.assertRaises(OSError) as context:
|
||||||
|
_, _ = os.timerfd_settime(fd, flags=flags, initial=initial, interval=interval)
|
||||||
|
self.assertEqual(context.exception.errno, errno.EINVAL)
|
||||||
|
|
||||||
|
with self.assertRaises(OSError) as context:
|
||||||
|
initial_ns = int( one_sec_in_nsec * initial )
|
||||||
|
interval_ns = int( one_sec_in_nsec * interval )
|
||||||
|
_, _ = os.timerfd_settime_ns(fd, flags=flags, initial=initial_ns, interval=interval_ns)
|
||||||
|
self.assertEqual(context.exception.errno, errno.EINVAL)
|
||||||
|
|
||||||
|
def test_timerfd_interval(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
# 1 second
|
||||||
|
initial_expiration = 1
|
||||||
|
# 0.5 second
|
||||||
|
interval = 0.5
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
|
||||||
|
|
||||||
|
# timerfd_gettime
|
||||||
|
next_expiration, interval2 = os.timerfd_gettime(fd)
|
||||||
|
self.assertAlmostEqual(interval2, interval, places=3)
|
||||||
|
self.assertAlmostEqual(next_expiration, initial_expiration, places=3)
|
||||||
|
|
||||||
|
count = 3
|
||||||
|
t = time.perf_counter()
|
||||||
|
for _ in range(count):
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
t = time.perf_counter() - t
|
||||||
|
|
||||||
|
total_time = initial_expiration + interval * (count - 1)
|
||||||
|
self.assertGreater(t, total_time)
|
||||||
|
|
||||||
|
# wait 3.5 time of interval
|
||||||
|
time.sleep( (count+0.5) * interval)
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, count)
|
||||||
|
|
||||||
|
def test_timerfd_TFD_TIMER_ABSTIME(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
now = time.clock_gettime(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
# 1 second later from now.
|
||||||
|
offset = 1
|
||||||
|
initial_expiration = now + offset
|
||||||
|
# not interval timer
|
||||||
|
interval = 0
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime(fd, flags=os.TFD_TIMER_ABSTIME, initial=initial_expiration, interval=interval)
|
||||||
|
|
||||||
|
# timerfd_gettime
|
||||||
|
# Note: timerfd_gettime returns relative values even if TFD_TIMER_ABSTIME is specified.
|
||||||
|
next_expiration, interval2 = os.timerfd_gettime(fd)
|
||||||
|
self.assertAlmostEqual(interval2, interval, places=3)
|
||||||
|
self.assertAlmostEqual(next_expiration, offset, places=3)
|
||||||
|
|
||||||
|
t = time.perf_counter()
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
t = time.perf_counter() - t
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
|
||||||
|
self.assertGreater(t, offset)
|
||||||
|
|
||||||
|
def test_timerfd_select(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
|
||||||
|
|
||||||
|
rfd, wfd, xfd = select.select([fd], [fd], [fd], 0)
|
||||||
|
self.assertEqual((rfd, wfd, xfd), ([], [], []))
|
||||||
|
|
||||||
|
# 0.25 second
|
||||||
|
initial_expiration = 0.25
|
||||||
|
# every 0.125 second
|
||||||
|
interval = 0.125
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
|
||||||
|
|
||||||
|
count = 3
|
||||||
|
t = time.perf_counter()
|
||||||
|
for _ in range(count):
|
||||||
|
rfd, wfd, xfd = select.select([fd], [fd], [fd], initial_expiration + interval)
|
||||||
|
self.assertEqual((rfd, wfd, xfd), ([fd], [], []))
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
t = time.perf_counter() - t
|
||||||
|
|
||||||
|
total_time = initial_expiration + interval * (count - 1)
|
||||||
|
self.assertGreater(t, total_time)
|
||||||
|
|
||||||
|
def test_timerfd_epoll(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
|
||||||
|
|
||||||
|
ep = select.epoll()
|
||||||
|
ep.register(fd, select.EPOLLIN)
|
||||||
|
self.addCleanup(ep.close)
|
||||||
|
|
||||||
|
# 0.25 second
|
||||||
|
initial_expiration = 0.25
|
||||||
|
# every 0.125 second
|
||||||
|
interval = 0.125
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime(fd, initial=initial_expiration, interval=interval)
|
||||||
|
|
||||||
|
count = 3
|
||||||
|
t = time.perf_counter()
|
||||||
|
for i in range(count):
|
||||||
|
timeout_margin = interval
|
||||||
|
if i == 0:
|
||||||
|
timeout = initial_expiration + interval + timeout_margin
|
||||||
|
else:
|
||||||
|
timeout = interval + timeout_margin
|
||||||
|
# epoll timeout is in seconds.
|
||||||
|
events = ep.poll(timeout)
|
||||||
|
self.assertEqual(events, [(fd, select.EPOLLIN)])
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
|
||||||
|
t = time.perf_counter() - t
|
||||||
|
|
||||||
|
total_time = initial_expiration + interval * (count - 1)
|
||||||
|
self.assertGreater(t, total_time)
|
||||||
|
ep.unregister(fd)
|
||||||
|
|
||||||
|
def test_timerfd_ns_initval(self):
|
||||||
|
one_sec_in_nsec = 10**9
|
||||||
|
limit_error = one_sec_in_nsec // 10**3
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
# 1st call
|
||||||
|
initial_expiration_ns = 0
|
||||||
|
interval_ns = one_sec_in_nsec // 1000
|
||||||
|
next_expiration_ns, interval_ns2 = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
|
||||||
|
self.assertEqual(interval_ns2, 0)
|
||||||
|
self.assertEqual(next_expiration_ns, 0)
|
||||||
|
|
||||||
|
# 2nd call
|
||||||
|
next_expiration_ns, interval_ns2 = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
|
||||||
|
self.assertEqual(interval_ns2, interval_ns)
|
||||||
|
self.assertEqual(next_expiration_ns, initial_expiration_ns)
|
||||||
|
|
||||||
|
# timerfd_gettime
|
||||||
|
next_expiration_ns, interval_ns2 = os.timerfd_gettime_ns(fd)
|
||||||
|
self.assertEqual(interval_ns2, interval_ns)
|
||||||
|
self.assertLessEqual(next_expiration_ns, initial_expiration_ns)
|
||||||
|
|
||||||
|
self.assertAlmostEqual(next_expiration_ns, initial_expiration_ns, delta=limit_error)
|
||||||
|
|
||||||
|
def test_timerfd_ns_interval(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
one_sec_in_nsec = 10**9
|
||||||
|
limit_error = one_sec_in_nsec // 10**3
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
# 1 second
|
||||||
|
initial_expiration_ns = one_sec_in_nsec
|
||||||
|
# every 0.5 second
|
||||||
|
interval_ns = one_sec_in_nsec // 2
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
|
||||||
|
|
||||||
|
# timerfd_gettime
|
||||||
|
next_expiration_ns, interval_ns2 = os.timerfd_gettime_ns(fd)
|
||||||
|
self.assertEqual(interval_ns2, interval_ns)
|
||||||
|
self.assertLessEqual(next_expiration_ns, initial_expiration_ns)
|
||||||
|
|
||||||
|
count = 3
|
||||||
|
t = time.perf_counter_ns()
|
||||||
|
for _ in range(count):
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
t = time.perf_counter_ns() - t
|
||||||
|
|
||||||
|
total_time_ns = initial_expiration_ns + interval_ns * (count - 1)
|
||||||
|
self.assertGreater(t, total_time_ns)
|
||||||
|
|
||||||
|
# wait 3.5 time of interval
|
||||||
|
time.sleep( (count+0.5) * interval_ns / one_sec_in_nsec)
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, count)
|
||||||
|
|
||||||
|
|
||||||
|
def test_timerfd_ns_TFD_TIMER_ABSTIME(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
one_sec_in_nsec = 10**9
|
||||||
|
limit_error = one_sec_in_nsec // 10**3
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
now_ns = time.clock_gettime_ns(time.CLOCK_REALTIME)
|
||||||
|
|
||||||
|
# 1 second later from now.
|
||||||
|
offset_ns = one_sec_in_nsec
|
||||||
|
initial_expiration_ns = now_ns + offset_ns
|
||||||
|
# not interval timer
|
||||||
|
interval_ns = 0
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime_ns(fd, flags=os.TFD_TIMER_ABSTIME, initial=initial_expiration_ns, interval=interval_ns)
|
||||||
|
|
||||||
|
# timerfd_gettime
|
||||||
|
# Note: timerfd_gettime returns relative values even if TFD_TIMER_ABSTIME is specified.
|
||||||
|
next_expiration_ns, interval_ns2 = os.timerfd_gettime_ns(fd)
|
||||||
|
self.assertLess(abs(interval_ns2 - interval_ns), limit_error)
|
||||||
|
self.assertLess(abs(next_expiration_ns - offset_ns), limit_error)
|
||||||
|
|
||||||
|
t = time.perf_counter_ns()
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
t = time.perf_counter_ns() - t
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
|
||||||
|
self.assertGreater(t, offset_ns)
|
||||||
|
|
||||||
|
def test_timerfd_ns_select(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
one_sec_in_nsec = 10**9
|
||||||
|
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
|
||||||
|
|
||||||
|
rfd, wfd, xfd = select.select([fd], [fd], [fd], 0)
|
||||||
|
self.assertEqual((rfd, wfd, xfd), ([], [], []))
|
||||||
|
|
||||||
|
# 0.25 second
|
||||||
|
initial_expiration_ns = one_sec_in_nsec // 4
|
||||||
|
# every 0.125 second
|
||||||
|
interval_ns = one_sec_in_nsec // 8
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
|
||||||
|
|
||||||
|
count = 3
|
||||||
|
t = time.perf_counter_ns()
|
||||||
|
for _ in range(count):
|
||||||
|
rfd, wfd, xfd = select.select([fd], [fd], [fd], (initial_expiration_ns + interval_ns) / 1e9 )
|
||||||
|
self.assertEqual((rfd, wfd, xfd), ([fd], [], []))
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
t = time.perf_counter_ns() - t
|
||||||
|
|
||||||
|
total_time_ns = initial_expiration_ns + interval_ns * (count - 1)
|
||||||
|
self.assertGreater(t, total_time_ns)
|
||||||
|
|
||||||
|
def test_timerfd_ns_epoll(self):
|
||||||
|
size = 8 # read 8 bytes
|
||||||
|
one_sec_in_nsec = 10**9
|
||||||
|
fd = self.timerfd_create(time.CLOCK_REALTIME, flags=os.TFD_NONBLOCK)
|
||||||
|
|
||||||
|
ep = select.epoll()
|
||||||
|
ep.register(fd, select.EPOLLIN)
|
||||||
|
self.addCleanup(ep.close)
|
||||||
|
|
||||||
|
# 0.25 second
|
||||||
|
initial_expiration_ns = one_sec_in_nsec // 4
|
||||||
|
# every 0.125 second
|
||||||
|
interval_ns = one_sec_in_nsec // 8
|
||||||
|
|
||||||
|
_, _ = os.timerfd_settime_ns(fd, initial=initial_expiration_ns, interval=interval_ns)
|
||||||
|
|
||||||
|
count = 3
|
||||||
|
t = time.perf_counter_ns()
|
||||||
|
for i in range(count):
|
||||||
|
timeout_margin_ns = interval_ns
|
||||||
|
if i == 0:
|
||||||
|
timeout_ns = initial_expiration_ns + interval_ns + timeout_margin_ns
|
||||||
|
else:
|
||||||
|
timeout_ns = interval_ns + timeout_margin_ns
|
||||||
|
|
||||||
|
# epoll timeout is in seconds.
|
||||||
|
events = ep.poll(timeout_ns / one_sec_in_nsec)
|
||||||
|
self.assertEqual(events, [(fd, select.EPOLLIN)])
|
||||||
|
n = os.read(fd, size)
|
||||||
|
count_signaled = int.from_bytes(n, byteorder=sys.byteorder)
|
||||||
|
self.assertEqual(count_signaled, 1)
|
||||||
|
|
||||||
|
t = time.perf_counter_ns() - t
|
||||||
|
|
||||||
|
total_time = initial_expiration_ns + interval_ns * (count - 1)
|
||||||
|
self.assertGreater(t, total_time)
|
||||||
|
ep.unregister(fd)
|
||||||
|
|
||||||
class OSErrorTests(unittest.TestCase):
|
class OSErrorTests(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Add :func:`os.timerfd_create`, :func:`os.timerfd_settime`, :func:`os.timerfd_gettime`, :func:`os.timerfd_settime_ns`, and :func:`os.timerfd_gettime_ns` to provide a low level interface for Linux's timer notification file descriptor.
|
|
@ -6022,6 +6022,376 @@ os_times(PyObject *module, PyObject *Py_UNUSED(ignored))
|
||||||
|
|
||||||
#endif /* defined(HAVE_TIMES) */
|
#endif /* defined(HAVE_TIMES) */
|
||||||
|
|
||||||
|
#if defined(HAVE_TIMERFD_CREATE)
|
||||||
|
|
||||||
|
PyDoc_STRVAR(os_timerfd_create__doc__,
|
||||||
|
"timerfd_create($module, clockid, /, *, flags=0)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Create and return a timer file descriptor.\n"
|
||||||
|
"\n"
|
||||||
|
" clockid\n"
|
||||||
|
" A valid clock ID constant as timer file descriptor.\n"
|
||||||
|
"\n"
|
||||||
|
" time.CLOCK_REALTIME\n"
|
||||||
|
" time.CLOCK_MONOTONIC\n"
|
||||||
|
" time.CLOCK_BOOTTIME\n"
|
||||||
|
" flags\n"
|
||||||
|
" 0 or a bit mask of os.TFD_NONBLOCK or os.TFD_CLOEXEC.\n"
|
||||||
|
"\n"
|
||||||
|
" os.TFD_NONBLOCK\n"
|
||||||
|
" If *TFD_NONBLOCK* is set as a flag, read doesn\'t blocks.\n"
|
||||||
|
" If *TFD_NONBLOCK* is not set as a flag, read block until the timer fires.\n"
|
||||||
|
"\n"
|
||||||
|
" os.TFD_CLOEXEC\n"
|
||||||
|
" If *TFD_CLOEXEC* is set as a flag, enable the close-on-exec flag");
|
||||||
|
|
||||||
|
#define OS_TIMERFD_CREATE_METHODDEF \
|
||||||
|
{"timerfd_create", _PyCFunction_CAST(os_timerfd_create), METH_FASTCALL|METH_KEYWORDS, os_timerfd_create__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_create_impl(PyObject *module, int clockid, int flags);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_create(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||||
|
|
||||||
|
#define NUM_KEYWORDS 1
|
||||||
|
static struct {
|
||||||
|
PyGC_Head _this_is_not_used;
|
||||||
|
PyObject_VAR_HEAD
|
||||||
|
PyObject *ob_item[NUM_KEYWORDS];
|
||||||
|
} _kwtuple = {
|
||||||
|
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
|
||||||
|
.ob_item = { &_Py_ID(flags), },
|
||||||
|
};
|
||||||
|
#undef NUM_KEYWORDS
|
||||||
|
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
|
||||||
|
|
||||||
|
#else // !Py_BUILD_CORE
|
||||||
|
# define KWTUPLE NULL
|
||||||
|
#endif // !Py_BUILD_CORE
|
||||||
|
|
||||||
|
static const char * const _keywords[] = {"", "flags", NULL};
|
||||||
|
static _PyArg_Parser _parser = {
|
||||||
|
.keywords = _keywords,
|
||||||
|
.fname = "timerfd_create",
|
||||||
|
.kwtuple = KWTUPLE,
|
||||||
|
};
|
||||||
|
#undef KWTUPLE
|
||||||
|
PyObject *argsbuf[2];
|
||||||
|
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
|
||||||
|
int clockid;
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
|
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
|
||||||
|
if (!args) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
clockid = PyLong_AsInt(args[0]);
|
||||||
|
if (clockid == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
flags = PyLong_AsInt(args[1]);
|
||||||
|
if (flags == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
skip_optional_kwonly:
|
||||||
|
return_value = os_timerfd_create_impl(module, clockid, flags);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(HAVE_TIMERFD_CREATE) */
|
||||||
|
|
||||||
|
#if defined(HAVE_TIMERFD_CREATE)
|
||||||
|
|
||||||
|
PyDoc_STRVAR(os_timerfd_settime__doc__,
|
||||||
|
"timerfd_settime($module, fd, /, *, flags=0, initial=0.0, interval=0.0)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Alter a timer file descriptor\'s internal timer in seconds.\n"
|
||||||
|
"\n"
|
||||||
|
" fd\n"
|
||||||
|
" A timer file descriptor.\n"
|
||||||
|
" flags\n"
|
||||||
|
" 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.\n"
|
||||||
|
" initial\n"
|
||||||
|
" The initial expiration time, in seconds.\n"
|
||||||
|
" interval\n"
|
||||||
|
" The timer\'s interval, in seconds.");
|
||||||
|
|
||||||
|
#define OS_TIMERFD_SETTIME_METHODDEF \
|
||||||
|
{"timerfd_settime", _PyCFunction_CAST(os_timerfd_settime), METH_FASTCALL|METH_KEYWORDS, os_timerfd_settime__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_settime_impl(PyObject *module, int fd, int flags, double initial,
|
||||||
|
double interval);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_settime(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||||
|
|
||||||
|
#define NUM_KEYWORDS 3
|
||||||
|
static struct {
|
||||||
|
PyGC_Head _this_is_not_used;
|
||||||
|
PyObject_VAR_HEAD
|
||||||
|
PyObject *ob_item[NUM_KEYWORDS];
|
||||||
|
} _kwtuple = {
|
||||||
|
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
|
||||||
|
.ob_item = { &_Py_ID(flags), &_Py_ID(initial), &_Py_ID(interval), },
|
||||||
|
};
|
||||||
|
#undef NUM_KEYWORDS
|
||||||
|
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
|
||||||
|
|
||||||
|
#else // !Py_BUILD_CORE
|
||||||
|
# define KWTUPLE NULL
|
||||||
|
#endif // !Py_BUILD_CORE
|
||||||
|
|
||||||
|
static const char * const _keywords[] = {"", "flags", "initial", "interval", NULL};
|
||||||
|
static _PyArg_Parser _parser = {
|
||||||
|
.keywords = _keywords,
|
||||||
|
.fname = "timerfd_settime",
|
||||||
|
.kwtuple = KWTUPLE,
|
||||||
|
};
|
||||||
|
#undef KWTUPLE
|
||||||
|
PyObject *argsbuf[4];
|
||||||
|
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
|
||||||
|
int fd;
|
||||||
|
int flags = 0;
|
||||||
|
double initial = 0.0;
|
||||||
|
double interval = 0.0;
|
||||||
|
|
||||||
|
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
|
||||||
|
if (!args) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
if (args[1]) {
|
||||||
|
flags = PyLong_AsInt(args[1]);
|
||||||
|
if (flags == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!--noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args[2]) {
|
||||||
|
if (PyFloat_CheckExact(args[2])) {
|
||||||
|
initial = PyFloat_AS_DOUBLE(args[2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
initial = PyFloat_AsDouble(args[2]);
|
||||||
|
if (initial == -1.0 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!--noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (PyFloat_CheckExact(args[3])) {
|
||||||
|
interval = PyFloat_AS_DOUBLE(args[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
interval = PyFloat_AsDouble(args[3]);
|
||||||
|
if (interval == -1.0 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skip_optional_kwonly:
|
||||||
|
return_value = os_timerfd_settime_impl(module, fd, flags, initial, interval);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(HAVE_TIMERFD_CREATE) */
|
||||||
|
|
||||||
|
#if defined(HAVE_TIMERFD_CREATE)
|
||||||
|
|
||||||
|
PyDoc_STRVAR(os_timerfd_settime_ns__doc__,
|
||||||
|
"timerfd_settime_ns($module, fd, /, *, flags=0, initial=0, interval=0)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Alter a timer file descriptor\'s internal timer in nanoseconds.\n"
|
||||||
|
"\n"
|
||||||
|
" fd\n"
|
||||||
|
" A timer file descriptor.\n"
|
||||||
|
" flags\n"
|
||||||
|
" 0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.\n"
|
||||||
|
" initial\n"
|
||||||
|
" initial expiration timing in seconds.\n"
|
||||||
|
" interval\n"
|
||||||
|
" interval for the timer in seconds.");
|
||||||
|
|
||||||
|
#define OS_TIMERFD_SETTIME_NS_METHODDEF \
|
||||||
|
{"timerfd_settime_ns", _PyCFunction_CAST(os_timerfd_settime_ns), METH_FASTCALL|METH_KEYWORDS, os_timerfd_settime_ns__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_settime_ns_impl(PyObject *module, int fd, int flags,
|
||||||
|
long long initial, long long interval);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_settime_ns(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
|
||||||
|
|
||||||
|
#define NUM_KEYWORDS 3
|
||||||
|
static struct {
|
||||||
|
PyGC_Head _this_is_not_used;
|
||||||
|
PyObject_VAR_HEAD
|
||||||
|
PyObject *ob_item[NUM_KEYWORDS];
|
||||||
|
} _kwtuple = {
|
||||||
|
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
|
||||||
|
.ob_item = { &_Py_ID(flags), &_Py_ID(initial), &_Py_ID(interval), },
|
||||||
|
};
|
||||||
|
#undef NUM_KEYWORDS
|
||||||
|
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
|
||||||
|
|
||||||
|
#else // !Py_BUILD_CORE
|
||||||
|
# define KWTUPLE NULL
|
||||||
|
#endif // !Py_BUILD_CORE
|
||||||
|
|
||||||
|
static const char * const _keywords[] = {"", "flags", "initial", "interval", NULL};
|
||||||
|
static _PyArg_Parser _parser = {
|
||||||
|
.keywords = _keywords,
|
||||||
|
.fname = "timerfd_settime_ns",
|
||||||
|
.kwtuple = KWTUPLE,
|
||||||
|
};
|
||||||
|
#undef KWTUPLE
|
||||||
|
PyObject *argsbuf[4];
|
||||||
|
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
|
||||||
|
int fd;
|
||||||
|
int flags = 0;
|
||||||
|
long long initial = 0;
|
||||||
|
long long interval = 0;
|
||||||
|
|
||||||
|
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
|
||||||
|
if (!args) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!_PyLong_FileDescriptor_Converter(args[0], &fd)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
if (args[1]) {
|
||||||
|
flags = PyLong_AsInt(args[1]);
|
||||||
|
if (flags == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!--noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (args[2]) {
|
||||||
|
initial = PyLong_AsLongLong(args[2]);
|
||||||
|
if (initial == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!--noptargs) {
|
||||||
|
goto skip_optional_kwonly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interval = PyLong_AsLongLong(args[3]);
|
||||||
|
if (interval == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
skip_optional_kwonly:
|
||||||
|
return_value = os_timerfd_settime_ns_impl(module, fd, flags, initial, interval);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(HAVE_TIMERFD_CREATE) */
|
||||||
|
|
||||||
|
#if defined(HAVE_TIMERFD_CREATE)
|
||||||
|
|
||||||
|
PyDoc_STRVAR(os_timerfd_gettime__doc__,
|
||||||
|
"timerfd_gettime($module, fd, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Return a tuple of a timer file descriptor\'s (interval, next expiration) in float seconds.\n"
|
||||||
|
"\n"
|
||||||
|
" fd\n"
|
||||||
|
" A timer file descriptor.");
|
||||||
|
|
||||||
|
#define OS_TIMERFD_GETTIME_METHODDEF \
|
||||||
|
{"timerfd_gettime", (PyCFunction)os_timerfd_gettime, METH_O, os_timerfd_gettime__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_gettime_impl(PyObject *module, int fd);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_gettime(PyObject *module, PyObject *arg)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (!_PyLong_FileDescriptor_Converter(arg, &fd)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = os_timerfd_gettime_impl(module, fd);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(HAVE_TIMERFD_CREATE) */
|
||||||
|
|
||||||
|
#if defined(HAVE_TIMERFD_CREATE)
|
||||||
|
|
||||||
|
PyDoc_STRVAR(os_timerfd_gettime_ns__doc__,
|
||||||
|
"timerfd_gettime_ns($module, fd, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n"
|
||||||
|
"Return a tuple of a timer file descriptor\'s (interval, next expiration) in nanoseconds.\n"
|
||||||
|
"\n"
|
||||||
|
" fd\n"
|
||||||
|
" A timer file descriptor.");
|
||||||
|
|
||||||
|
#define OS_TIMERFD_GETTIME_NS_METHODDEF \
|
||||||
|
{"timerfd_gettime_ns", (PyCFunction)os_timerfd_gettime_ns, METH_O, os_timerfd_gettime_ns__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_gettime_ns_impl(PyObject *module, int fd);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_gettime_ns(PyObject *module, PyObject *arg)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if (!_PyLong_FileDescriptor_Converter(arg, &fd)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = os_timerfd_gettime_ns_impl(module, fd);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* defined(HAVE_TIMERFD_CREATE) */
|
||||||
|
|
||||||
#if defined(HAVE_GETSID)
|
#if defined(HAVE_GETSID)
|
||||||
|
|
||||||
PyDoc_STRVAR(os_getsid__doc__,
|
PyDoc_STRVAR(os_getsid__doc__,
|
||||||
|
@ -11761,6 +12131,26 @@ exit:
|
||||||
#define OS_TIMES_METHODDEF
|
#define OS_TIMES_METHODDEF
|
||||||
#endif /* !defined(OS_TIMES_METHODDEF) */
|
#endif /* !defined(OS_TIMES_METHODDEF) */
|
||||||
|
|
||||||
|
#ifndef OS_TIMERFD_CREATE_METHODDEF
|
||||||
|
#define OS_TIMERFD_CREATE_METHODDEF
|
||||||
|
#endif /* !defined(OS_TIMERFD_CREATE_METHODDEF) */
|
||||||
|
|
||||||
|
#ifndef OS_TIMERFD_SETTIME_METHODDEF
|
||||||
|
#define OS_TIMERFD_SETTIME_METHODDEF
|
||||||
|
#endif /* !defined(OS_TIMERFD_SETTIME_METHODDEF) */
|
||||||
|
|
||||||
|
#ifndef OS_TIMERFD_SETTIME_NS_METHODDEF
|
||||||
|
#define OS_TIMERFD_SETTIME_NS_METHODDEF
|
||||||
|
#endif /* !defined(OS_TIMERFD_SETTIME_NS_METHODDEF) */
|
||||||
|
|
||||||
|
#ifndef OS_TIMERFD_GETTIME_METHODDEF
|
||||||
|
#define OS_TIMERFD_GETTIME_METHODDEF
|
||||||
|
#endif /* !defined(OS_TIMERFD_GETTIME_METHODDEF) */
|
||||||
|
|
||||||
|
#ifndef OS_TIMERFD_GETTIME_NS_METHODDEF
|
||||||
|
#define OS_TIMERFD_GETTIME_NS_METHODDEF
|
||||||
|
#endif /* !defined(OS_TIMERFD_GETTIME_NS_METHODDEF) */
|
||||||
|
|
||||||
#ifndef OS_GETSID_METHODDEF
|
#ifndef OS_GETSID_METHODDEF
|
||||||
#define OS_GETSID_METHODDEF
|
#define OS_GETSID_METHODDEF
|
||||||
#endif /* !defined(OS_GETSID_METHODDEF) */
|
#endif /* !defined(OS_GETSID_METHODDEF) */
|
||||||
|
@ -12024,4 +12414,4 @@ exit:
|
||||||
#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
||||||
#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
||||||
#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
|
#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
|
||||||
/*[clinic end generated code: output=a36904281a8a7507 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=7c3058135ed49d20 input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -550,6 +550,11 @@ extern char *ctermid_r(char *);
|
||||||
# include <sys/eventfd.h>
|
# include <sys/eventfd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* timerfd_create() */
|
||||||
|
#ifdef HAVE_SYS_TIMERFD_H
|
||||||
|
# include <sys/timerfd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _Py_MEMORY_SANITIZER
|
#ifdef _Py_MEMORY_SANITIZER
|
||||||
# include <sanitizer/msan_interface.h>
|
# include <sanitizer/msan_interface.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -10096,6 +10101,227 @@ os_times_impl(PyObject *module)
|
||||||
#endif /* HAVE_TIMES */
|
#endif /* HAVE_TIMES */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(HAVE_TIMERFD_CREATE)
|
||||||
|
#define ONE_SECOND_IN_NS (1000 * 1000 * 1000)
|
||||||
|
#define EXTRACT_NSEC(value) (long)( ( (double)(value) - (time_t)(value) ) * 1e9)
|
||||||
|
#define CONVERT_SEC_AND_NSEC_TO_DOUBLE(sec, nsec) ( (double)(sec) + (double)(nsec) * 1e-9 )
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
build_itimerspec(const struct itimerspec* curr_value)
|
||||||
|
{
|
||||||
|
double _value = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_value.tv_sec,
|
||||||
|
curr_value->it_value.tv_nsec);
|
||||||
|
PyObject *value = PyFloat_FromDouble(_value);
|
||||||
|
if (value == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
double _interval = CONVERT_SEC_AND_NSEC_TO_DOUBLE(curr_value->it_interval.tv_sec,
|
||||||
|
curr_value->it_interval.tv_nsec);
|
||||||
|
PyObject *interval = PyFloat_FromDouble(_interval);
|
||||||
|
if (interval == NULL) {
|
||||||
|
Py_DECREF(value);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *tuple = PyTuple_Pack(2, value, interval);
|
||||||
|
Py_DECREF(interval);
|
||||||
|
Py_DECREF(value);
|
||||||
|
return tuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
build_itimerspec_ns(const struct itimerspec* curr_value)
|
||||||
|
{
|
||||||
|
_PyTime_t value, interval;
|
||||||
|
if (_PyTime_FromTimespec(&value, &curr_value->it_value) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (_PyTime_FromTimespec(&interval, &curr_value->it_interval) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return Py_BuildValue("LL", value, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
os.timerfd_create
|
||||||
|
|
||||||
|
clockid: int
|
||||||
|
A valid clock ID constant as timer file descriptor.
|
||||||
|
|
||||||
|
time.CLOCK_REALTIME
|
||||||
|
time.CLOCK_MONOTONIC
|
||||||
|
time.CLOCK_BOOTTIME
|
||||||
|
/
|
||||||
|
*
|
||||||
|
flags: int = 0
|
||||||
|
0 or a bit mask of os.TFD_NONBLOCK or os.TFD_CLOEXEC.
|
||||||
|
|
||||||
|
os.TFD_NONBLOCK
|
||||||
|
If *TFD_NONBLOCK* is set as a flag, read doesn't blocks.
|
||||||
|
If *TFD_NONBLOCK* is not set as a flag, read block until the timer fires.
|
||||||
|
|
||||||
|
os.TFD_CLOEXEC
|
||||||
|
If *TFD_CLOEXEC* is set as a flag, enable the close-on-exec flag
|
||||||
|
|
||||||
|
Create and return a timer file descriptor.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_create_impl(PyObject *module, int clockid, int flags)
|
||||||
|
/*[clinic end generated code: output=1caae80fb168004a input=64b7020c5ac0b8f4]*/
|
||||||
|
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
flags |= TFD_CLOEXEC; // PEP 446: always create non-inheritable FD
|
||||||
|
fd = timerfd_create(clockid, flags);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (fd == -1) {
|
||||||
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
return PyLong_FromLong(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
os.timerfd_settime
|
||||||
|
|
||||||
|
fd: fildes
|
||||||
|
A timer file descriptor.
|
||||||
|
/
|
||||||
|
*
|
||||||
|
flags: int = 0
|
||||||
|
0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.
|
||||||
|
initial: double = 0.0
|
||||||
|
The initial expiration time, in seconds.
|
||||||
|
interval: double = 0.0
|
||||||
|
The timer's interval, in seconds.
|
||||||
|
|
||||||
|
Alter a timer file descriptor's internal timer in seconds.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_settime_impl(PyObject *module, int fd, int flags, double initial,
|
||||||
|
double interval)
|
||||||
|
/*[clinic end generated code: output=0dda31115317adb9 input=6c24e47e7a4d799e]*/
|
||||||
|
{
|
||||||
|
struct itimerspec new_value;
|
||||||
|
struct itimerspec old_value;
|
||||||
|
int result;
|
||||||
|
if (_PyTime_AsTimespec(_PyTime_FromSecondsDouble(initial, _PyTime_ROUND_FLOOR), &new_value.it_value) < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "invalid initial value");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (_PyTime_AsTimespec(_PyTime_FromSecondsDouble(interval, _PyTime_ROUND_FLOOR), &new_value.it_interval) < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "invalid interval value");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
result = timerfd_settime(fd, flags, &new_value, &old_value);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (result == -1) {
|
||||||
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
return build_itimerspec(&old_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
os.timerfd_settime_ns
|
||||||
|
|
||||||
|
fd: fildes
|
||||||
|
A timer file descriptor.
|
||||||
|
/
|
||||||
|
*
|
||||||
|
flags: int = 0
|
||||||
|
0 or a bit mask of TFD_TIMER_ABSTIME or TFD_TIMER_CANCEL_ON_SET.
|
||||||
|
initial: long_long = 0
|
||||||
|
initial expiration timing in seconds.
|
||||||
|
interval: long_long = 0
|
||||||
|
interval for the timer in seconds.
|
||||||
|
|
||||||
|
Alter a timer file descriptor's internal timer in nanoseconds.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_settime_ns_impl(PyObject *module, int fd, int flags,
|
||||||
|
long long initial, long long interval)
|
||||||
|
/*[clinic end generated code: output=6273ec7d7b4cc0b3 input=261e105d6e42f5bc]*/
|
||||||
|
{
|
||||||
|
struct itimerspec new_value;
|
||||||
|
struct itimerspec old_value;
|
||||||
|
int result;
|
||||||
|
if (_PyTime_AsTimespec(initial, &new_value.it_value) < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "invalid initial value");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (_PyTime_AsTimespec(interval, &new_value.it_interval) < 0) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "invalid interval value");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
result = timerfd_settime(fd, flags, &new_value, &old_value);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (result == -1) {
|
||||||
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
return build_itimerspec_ns(&old_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
os.timerfd_gettime
|
||||||
|
|
||||||
|
fd: fildes
|
||||||
|
A timer file descriptor.
|
||||||
|
/
|
||||||
|
|
||||||
|
Return a tuple of a timer file descriptor's (interval, next expiration) in float seconds.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_gettime_impl(PyObject *module, int fd)
|
||||||
|
/*[clinic end generated code: output=ec5a94a66cfe6ab4 input=8148e3430870da1c]*/
|
||||||
|
{
|
||||||
|
struct itimerspec curr_value;
|
||||||
|
int result;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
result = timerfd_gettime(fd, &curr_value);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (result == -1) {
|
||||||
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
return build_itimerspec(&curr_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
os.timerfd_gettime_ns
|
||||||
|
|
||||||
|
fd: fildes
|
||||||
|
A timer file descriptor.
|
||||||
|
/
|
||||||
|
|
||||||
|
Return a tuple of a timer file descriptor's (interval, next expiration) in nanoseconds.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
os_timerfd_gettime_ns_impl(PyObject *module, int fd)
|
||||||
|
/*[clinic end generated code: output=580633a4465f39fe input=a825443e4c6b40ac]*/
|
||||||
|
{
|
||||||
|
struct itimerspec curr_value;
|
||||||
|
int result;
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
result = timerfd_gettime(fd, &curr_value);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
if (result == -1) {
|
||||||
|
return PyErr_SetFromErrno(PyExc_OSError);
|
||||||
|
}
|
||||||
|
return build_itimerspec_ns(&curr_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef ONE_SECOND_IN_NS
|
||||||
|
#undef EXTRACT_NSEC
|
||||||
|
|
||||||
|
#endif /* HAVE_TIMERFD_CREATE */
|
||||||
|
|
||||||
#ifdef HAVE_GETSID
|
#ifdef HAVE_GETSID
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
os.getsid
|
os.getsid
|
||||||
|
@ -16028,6 +16254,11 @@ static PyMethodDef posix_methods[] = {
|
||||||
OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
OS_WAITSTATUS_TO_EXITCODE_METHODDEF
|
||||||
OS_SETNS_METHODDEF
|
OS_SETNS_METHODDEF
|
||||||
OS_UNSHARE_METHODDEF
|
OS_UNSHARE_METHODDEF
|
||||||
|
OS_TIMERFD_CREATE_METHODDEF
|
||||||
|
OS_TIMERFD_SETTIME_METHODDEF
|
||||||
|
OS_TIMERFD_SETTIME_NS_METHODDEF
|
||||||
|
OS_TIMERFD_GETTIME_METHODDEF
|
||||||
|
OS_TIMERFD_GETTIME_NS_METHODDEF
|
||||||
|
|
||||||
OS__PATH_ISDEVDRIVE_METHODDEF
|
OS__PATH_ISDEVDRIVE_METHODDEF
|
||||||
OS__PATH_ISDIR_METHODDEF
|
OS__PATH_ISDIR_METHODDEF
|
||||||
|
@ -16343,6 +16574,19 @@ all_ins(PyObject *m)
|
||||||
if (PyModule_AddIntMacro(m, SF_NOCACHE)) return -1;
|
if (PyModule_AddIntMacro(m, SF_NOCACHE)) return -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef TFD_NONBLOCK
|
||||||
|
if (PyModule_AddIntMacro(m, TFD_NONBLOCK)) return -1;
|
||||||
|
#endif
|
||||||
|
#ifdef TFD_CLOEXEC
|
||||||
|
if (PyModule_AddIntMacro(m, TFD_CLOEXEC)) return -1;
|
||||||
|
#endif
|
||||||
|
#ifdef TFD_TIMER_ABSTIME
|
||||||
|
if (PyModule_AddIntMacro(m, TFD_TIMER_ABSTIME)) return -1;
|
||||||
|
#endif
|
||||||
|
#ifdef TFD_TIMER_CANCEL_ON_SET
|
||||||
|
if (PyModule_AddIntMacro(m, TFD_TIMER_CANCEL_ON_SET)) return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* constants for posix_fadvise */
|
/* constants for posix_fadvise */
|
||||||
#ifdef POSIX_FADV_NORMAL
|
#ifdef POSIX_FADV_NORMAL
|
||||||
if (PyModule_AddIntMacro(m, POSIX_FADV_NORMAL)) return -1;
|
if (PyModule_AddIntMacro(m, POSIX_FADV_NORMAL)) return -1;
|
||||||
|
@ -16741,6 +16985,10 @@ static const struct have_function {
|
||||||
{"HAVE_EVENTFD", NULL},
|
{"HAVE_EVENTFD", NULL},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TIMERFD_CREATE
|
||||||
|
{"HAVE_TIMERFD_CREATE", NULL},
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_FACCESSAT
|
#ifdef HAVE_FACCESSAT
|
||||||
{ "HAVE_FACCESSAT", probe_faccessat },
|
{ "HAVE_FACCESSAT", probe_faccessat },
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -470,7 +470,7 @@ _PyTime_FromNanosecondsObject(_PyTime_t *tp, PyObject *obj)
|
||||||
|
|
||||||
#ifdef HAVE_CLOCK_GETTIME
|
#ifdef HAVE_CLOCK_GETTIME
|
||||||
static int
|
static int
|
||||||
pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise_exc)
|
pytime_fromtimespec(_PyTime_t *tp, const struct timespec *ts, int raise_exc)
|
||||||
{
|
{
|
||||||
_PyTime_t t, tv_nsec;
|
_PyTime_t t, tv_nsec;
|
||||||
|
|
||||||
|
@ -493,7 +493,7 @@ pytime_fromtimespec(_PyTime_t *tp, struct timespec *ts, int raise_exc)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts)
|
_PyTime_FromTimespec(_PyTime_t *tp, const struct timespec *ts)
|
||||||
{
|
{
|
||||||
return pytime_fromtimespec(tp, ts, 1);
|
return pytime_fromtimespec(tp, ts, 1);
|
||||||
}
|
}
|
||||||
|
@ -635,6 +635,16 @@ _PyTime_AsNanosecondsObject(_PyTime_t t)
|
||||||
return PyLong_FromLongLong((long long)ns);
|
return PyLong_FromLongLong((long long)ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_PyTime_t
|
||||||
|
_PyTime_FromSecondsDouble(double seconds, _PyTime_round_t round)
|
||||||
|
{
|
||||||
|
_PyTime_t tp;
|
||||||
|
if(pytime_from_double(&tp, seconds, round, SEC_TO_NS) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static _PyTime_t
|
static _PyTime_t
|
||||||
pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
|
pytime_divide_round_up(const _PyTime_t t, const _PyTime_t k)
|
||||||
|
|
|
@ -10709,6 +10709,12 @@ if test "x$ac_cv_header_sys_times_h" = xyes
|
||||||
then :
|
then :
|
||||||
printf "%s\n" "#define HAVE_SYS_TIMES_H 1" >>confdefs.h
|
printf "%s\n" "#define HAVE_SYS_TIMES_H 1" >>confdefs.h
|
||||||
|
|
||||||
|
fi
|
||||||
|
ac_fn_c_check_header_compile "$LINENO" "sys/timerfd.h" "ac_cv_header_sys_timerfd_h" "$ac_includes_default"
|
||||||
|
if test "x$ac_cv_header_sys_timerfd_h" = xyes
|
||||||
|
then :
|
||||||
|
printf "%s\n" "#define HAVE_SYS_TIMERFD_H 1" >>confdefs.h
|
||||||
|
|
||||||
fi
|
fi
|
||||||
ac_fn_c_check_header_compile "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
|
ac_fn_c_check_header_compile "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
|
||||||
if test "x$ac_cv_header_sys_types_h" = xyes
|
if test "x$ac_cv_header_sys_types_h" = xyes
|
||||||
|
@ -18781,6 +18787,50 @@ fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for timerfd_create" >&5
|
||||||
|
printf %s "checking for timerfd_create... " >&6; }
|
||||||
|
if test ${ac_cv_func_timerfd_create+y}
|
||||||
|
then :
|
||||||
|
printf %s "(cached) " >&6
|
||||||
|
else $as_nop
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_TIMERFD_H
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
void *x=timerfd_create
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_c_try_compile "$LINENO"
|
||||||
|
then :
|
||||||
|
ac_cv_func_timerfd_create=yes
|
||||||
|
else $as_nop
|
||||||
|
ac_cv_func_timerfd_create=no
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||||
|
|
||||||
|
fi
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_timerfd_create" >&5
|
||||||
|
printf "%s\n" "$ac_cv_func_timerfd_create" >&6; }
|
||||||
|
if test "x$ac_cv_func_timerfd_create" = xyes
|
||||||
|
then :
|
||||||
|
|
||||||
|
printf "%s\n" "#define HAVE_TIMERFD_CREATE 1" >>confdefs.h
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# On some systems (eg. FreeBSD 5), we would find a definition of the
|
# On some systems (eg. FreeBSD 5), we would find a definition of the
|
||||||
# functions ctermid_r, setgroups in the library, but no prototype
|
# functions ctermid_r, setgroups in the library, but no prototype
|
||||||
# (e.g. because we use _XOPEN_SOURCE). See whether we can take their
|
# (e.g. because we use _XOPEN_SOURCE). See whether we can take their
|
||||||
|
|
|
@ -2691,7 +2691,7 @@ AC_CHECK_HEADERS([ \
|
||||||
sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \
|
sys/endian.h sys/epoll.h sys/event.h sys/eventfd.h sys/file.h sys/ioctl.h sys/kern_control.h \
|
||||||
sys/loadavg.h sys/lock.h sys/memfd.h sys/mkdev.h sys/mman.h sys/modem.h sys/param.h sys/poll.h \
|
sys/loadavg.h sys/lock.h sys/memfd.h sys/mkdev.h sys/mman.h sys/modem.h sys/param.h sys/poll.h \
|
||||||
sys/random.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/soundcard.h sys/stat.h \
|
sys/random.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/soundcard.h sys/stat.h \
|
||||||
sys/statvfs.h sys/sys_domain.h sys/syscall.h sys/sysmacros.h sys/termio.h sys/time.h sys/times.h \
|
sys/statvfs.h sys/sys_domain.h sys/syscall.h sys/sysmacros.h sys/termio.h sys/time.h sys/times.h sys/timerfd.h \
|
||||||
sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h sys/xattr.h sysexits.h syslog.h \
|
sys/types.h sys/uio.h sys/un.h sys/utsname.h sys/wait.h sys/xattr.h sysexits.h syslog.h \
|
||||||
termios.h util.h utime.h utmp.h \
|
termios.h util.h utime.h utmp.h \
|
||||||
])
|
])
|
||||||
|
@ -4744,6 +4744,13 @@ PY_CHECK_FUNC([eventfd], [
|
||||||
#endif
|
#endif
|
||||||
])
|
])
|
||||||
|
|
||||||
|
PY_CHECK_FUNC([timerfd_create], [
|
||||||
|
#ifdef HAVE_SYS_TIMERFD_H
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#endif
|
||||||
|
],
|
||||||
|
[HAVE_TIMERFD_CREATE])
|
||||||
|
|
||||||
# On some systems (eg. FreeBSD 5), we would find a definition of the
|
# On some systems (eg. FreeBSD 5), we would find a definition of the
|
||||||
# functions ctermid_r, setgroups in the library, but no prototype
|
# functions ctermid_r, setgroups in the library, but no prototype
|
||||||
# (e.g. because we use _XOPEN_SOURCE). See whether we can take their
|
# (e.g. because we use _XOPEN_SOURCE). See whether we can take their
|
||||||
|
|
|
@ -1363,6 +1363,9 @@
|
||||||
/* Define to 1 if you have the <sys/termio.h> header file. */
|
/* Define to 1 if you have the <sys/termio.h> header file. */
|
||||||
#undef HAVE_SYS_TERMIO_H
|
#undef HAVE_SYS_TERMIO_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/timerfd.h> header file. */
|
||||||
|
#undef HAVE_SYS_TIMERFD_H
|
||||||
|
|
||||||
/* Define to 1 if you have the <sys/times.h> header file. */
|
/* Define to 1 if you have the <sys/times.h> header file. */
|
||||||
#undef HAVE_SYS_TIMES_H
|
#undef HAVE_SYS_TIMES_H
|
||||||
|
|
||||||
|
@ -1405,6 +1408,9 @@
|
||||||
/* Define to 1 if you have the `timegm' function. */
|
/* Define to 1 if you have the `timegm' function. */
|
||||||
#undef HAVE_TIMEGM
|
#undef HAVE_TIMEGM
|
||||||
|
|
||||||
|
/* Define if you have the 'timerfd_create' function. */
|
||||||
|
#undef HAVE_TIMERFD_CREATE
|
||||||
|
|
||||||
/* Define to 1 if you have the `times' function. */
|
/* Define to 1 if you have the `times' function. */
|
||||||
#undef HAVE_TIMES
|
#undef HAVE_TIMES
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue