diff --git a/docs/source/reference-hazmat.rst b/docs/source/reference-hazmat.rst index 8fd0a85a94..1ba6725351 100644 --- a/docs/source/reference-hazmat.rst +++ b/docs/source/reference-hazmat.rst @@ -194,6 +194,30 @@ Unix-like systems provide the following functions: become writable. +.. function:: wait_priority(fd) + :async: + + Block until there is an "exceptional condition" on the given file + descriptor. + + .. warning:: + + This method uses the ``POLLPRI`` flag to the ``epoll`` system call. + Its semantics are highly specific to the type of file descriptor + you're passing to it. + + This test may be used to + * wait for changes to hardware inputs (Linux GPIO) + * notice changed state of a PTY slave by the master + * … + + :arg fd: + integer file descriptor, or else an object with a ``fileno()`` method + :raises trio.ResourceBusyError: + if another task is already waiting for an exceptional condition on + the given fd. + + Kqueue-specific API ------------------- diff --git a/trio/_core/_io_epoll.py b/trio/_core/_io_epoll.py index da605dd701..1d425c1c51 100644 --- a/trio/_core/_io_epoll.py +++ b/trio/_core/_io_epoll.py @@ -9,6 +9,7 @@ class _EpollStatistics: tasks_waiting_read = attr.ib() tasks_waiting_write = attr.ib() + tasks_waiting_prio = attr.ib() backend = attr.ib(default="epoll") @@ -16,6 +17,7 @@ class _EpollStatistics: class EpollWaiters: read_task = attr.ib(default=None) write_task = attr.ib(default=None) + prio_task = attr.ib(default=None) def flags(self): flags = 0 @@ -23,6 +25,8 @@ def flags(self): flags |= select.EPOLLIN if self.write_task is not None: flags |= select.EPOLLOUT + if self.prio_task is not None: + flags |= select.EPOLLPRI if not flags: return None # XX not sure if EPOLLEXCLUSIVE is actually safe... I think @@ -47,14 +51,18 @@ class EpollIOManager: def statistics(self): tasks_waiting_read = 0 tasks_waiting_write = 0 + tasks_waiting_prio = 0 for waiter in self._registered.values(): if waiter.read_task is not None: tasks_waiting_read += 1 if waiter.write_task is not None: tasks_waiting_write += 1 + if waiter.prio_task is not None: + tasks_waiting_prio += 1 return _EpollStatistics( tasks_waiting_read=tasks_waiting_read, tasks_waiting_write=tasks_waiting_write, + tasks_waiting_prio=tasks_waiting_prio, ) def close(self): @@ -70,12 +78,19 @@ def handle_io(self, timeout): # Clever hack stolen from selectors.EpollSelector: an event # with EPOLLHUP or EPOLLERR flags wakes both readers and # writers. - if flags & ~select.EPOLLIN and waiters.write_task is not None: - _core.reschedule(waiters.write_task) - waiters.write_task = None - if flags & ~select.EPOLLOUT and waiters.read_task is not None: + if flags & ~( + select.EPOLLOUT | select.EPOLLPRI + ) and waiters.read_task is not None: _core.reschedule(waiters.read_task) waiters.read_task = None + if flags & ~( + select.EPOLLIN | select.EPOLLPRI + ) and waiters.write_task is not None: + _core.reschedule(waiters.write_task) + waiters.write_task = None + if flags & select.EPOLLPRI and waiters.prio_task is not None: + _core.reschedule(waiters.prio_task) + waiters.prio_task = None self._update_registrations(fd, True) def _update_registrations(self, fd, currently_registered): @@ -122,3 +137,7 @@ async def wait_readable(self, fd): @_public async def wait_writable(self, fd): await self._epoll_wait(fd, "write_task") + + @_public + async def wait_priority(self, fd): + await self._epoll_wait(fd, "prio_task") diff --git a/trio/hazmat.py b/trio/hazmat.py index d57d6b3d3c..5c3f1b4ad5 100644 --- a/trio/hazmat.py +++ b/trio/hazmat.py @@ -22,6 +22,7 @@ "add_instrument", "current_clock", "current_statistics", + "wait_priority", "wait_writable", "wait_readable", "ParkingLot",