diff options
author | Helmut Grohne <helmut@subdivi.de> | 2025-05-24 07:18:18 +0200 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2025-05-24 07:18:18 +0200 |
commit | 82e056900471621799037d3e1df30d796236c9a3 (patch) | |
tree | 2175788691a2b750a05c1962416e1b40b4dc5f0a | |
parent | 5791cb3074e15d8458339fecee9f23ae30d9d244 (diff) | |
download | python-linuxnamespaces-82e056900471621799037d3e1df30d796236c9a3.tar.gz |
SignalFD.areaditer: support iterative reading
-rw-r--r-- | linuxnamespaces/syscalls.py | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/linuxnamespaces/syscalls.py b/linuxnamespaces/syscalls.py index 9528b07..c4edad2 100644 --- a/linuxnamespaces/syscalls.py +++ b/linuxnamespaces/syscalls.py @@ -762,6 +762,8 @@ def setns(fd: int, nstype: CloneFlags = CloneFlags.NONE) -> None: class SignalFD: """Represent a file descriptor returned from signalfd(2).""" + _ReadIterFut = asyncio.Future[tuple[list[SignalFDSigInfo], "_ReadIterFut"]] + def __init__( self, sigmask: typing.Iterable[signal.Signals], @@ -801,7 +803,7 @@ class SignalFD: res = self.readv(1) return res[0] - def __handle_readable( + def __handle_read( self, fd: int, fut: asyncio.Future[SignalFDSigInfo] ) -> None: try: @@ -826,9 +828,41 @@ class SignalFD: raise ValueError("attempt to read from closed signalfd") loop = asyncio.get_running_loop() fut: asyncio.Future[SignalFDSigInfo] = loop.create_future() - loop.add_reader(self.fd, self.__handle_readable, self.fd, fut) + loop.add_reader(self.fd, self.__handle_read, self.fd, fut) return fut + def __handle_readiter(self, fd: int, fut: _ReadIterFut) -> None: + loop = fut.get_loop() + try: + if fd != self.fd: + raise RuntimeError("SignalFD file descriptor changed") + try: + # Attempt to read a full page worth of queued signals. + results = self.readv(32) + except OSError as err: + if err.errno == errno.EAGAIN: + return + raise + except Exception as exc: + loop.remove_reader(fd) + fut.set_exception(exc) + else: + nextfut: SignalFD._ReadIterFut = loop.create_future() + loop.add_reader(fd, self.__handle_readiter, self.fd, nextfut) + fut.set_result((results, nextfut)) + + async def areaditer(self) -> typing.AsyncIterator[SignalFDSigInfo]: + """Asynchronously read signals from the signalfd forever.""" + if self.fd < 0: + raise ValueError("attempt to read from closed signalfd") + loop = asyncio.get_running_loop() + fut: SignalFD._ReadIterFut = loop.create_future() + loop.add_reader(self.fd, self.__handle_readiter, self.fd, fut) + while True: + results, fut = await fut + for result in results: + yield result + def fileno(self) -> FileDescriptor: """Return the underlying file descriptor.""" return self.fd |