Fix resize and poller races; add coverage

- Fix resize message handling when session already exists
- Guard poller selector.modify against removed fds
- Handle send_bytes race when master_fd closes
- Add tests for resize edge case, poller write KeyError, send_bytes race
This commit is contained in:
GitHub Copilot
2026-01-26 20:07:40 +00:00
parent 245849ba9f
commit 63e8cba0ac
6 changed files with 56 additions and 6 deletions
+3 -3
View File
@@ -346,9 +346,9 @@ class LocalServer:
if msg_type == "stdin":
await self._handle_stdin(envelope, route_key, ws)
elif msg_type == "resize":
if not session_created and await self._handle_resize(envelope, route_key, ws):
session_created = True
elif session_created:
if not session_created:
session_created = await self._handle_resize(envelope, route_key, ws)
else:
await self._handle_resize(envelope, route_key, ws)
elif msg_type == "ping":
await self._handle_ping(envelope, route_key, ws)
+11 -1
View File
@@ -62,7 +62,17 @@ class Poller(Thread):
self._write_queues[file_descriptor] = deque()
new_write = Write(data)
self._write_queues[file_descriptor].append(new_write)
self._selector.modify(file_descriptor, selectors.EVENT_READ | selectors.EVENT_WRITE)
try:
self._selector.modify(file_descriptor, selectors.EVENT_READ | selectors.EVENT_WRITE)
except KeyError:
# File descriptor removed concurrently
new_write.done_event.set()
return
await new_write.done_event.wait()
def set_loop(self, loop: asyncio.AbstractEventLoop) -> None:
+6 -2
View File
@@ -253,9 +253,13 @@ class TerminalSession(Session):
os.close(fd)
async def send_bytes(self, data: bytes) -> bool:
if self.master_fd is None:
fd = self.master_fd
if fd is None:
return False
try:
await self.poller.write(fd, data)
except (KeyError, OSError):
return False
await self.poller.write(self.master_fd, data)
return True
async def send_meta(self, data: Meta) -> bool: