Skip to content

Commit 1a7cf26

Browse files
ceharrisaaugustin
authored andcommitted
Use selectors instead of select.poll in sync.WebSocket Server for multi-platform support (#1349)
* use multiplatform selector instead of poll * don't use os.pipe with the I/O multiplexing selector on win32 On the Win32 platform, only sockets can be used with I/O multiplexing (such as that performed by selectors.DefaultSelector); the pipe cannot be added to the selector. However, on the win32 platform, simply closing the listener socket is enough to cause the call to select to return -- the additional pipe is redundant. On Mac OS X (and possibly other BSD derivatives), closing the listener socket isn't enough. In the interest of maximum compatibility, we simply disable the use of os.pipe on the Win32 platform. * exclude platform checks for win32 from coverage testing
1 parent 0ce16b2 commit 1a7cf26

File tree

2 files changed

+13
-8
lines changed

2 files changed

+13
-8
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ source = [
5454
exclude_lines = [
5555
"except ImportError:",
5656
"if self.debug:",
57+
"if sys.platform != \"win32\":",
5758
"pragma: no cover",
5859
"raise AssertionError",
5960
"raise NotImplementedError",

src/websockets/sync/server.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
import http
44
import logging
55
import os
6-
import select
6+
import selectors
77
import socket
88
import ssl
9+
import sys
910
import threading
1011
from types import TracebackType
1112
from typing import Any, Callable, Optional, Sequence, Type
@@ -200,7 +201,8 @@ def __init__(
200201
if logger is None:
201202
logger = logging.getLogger("websockets.server")
202203
self.logger = logger
203-
self.shutdown_watcher, self.shutdown_notifier = os.pipe()
204+
if sys.platform != "win32":
205+
self.shutdown_watcher, self.shutdown_notifier = os.pipe()
204206

205207
def serve_forever(self) -> None:
206208
"""
@@ -215,15 +217,16 @@ def serve_forever(self) -> None:
215217
server.serve_forever()
216218
217219
"""
218-
poller = select.poll()
219-
poller.register(self.socket)
220-
poller.register(self.shutdown_watcher)
220+
poller = selectors.DefaultSelector()
221+
poller.register(self.socket, selectors.EVENT_READ)
222+
if sys.platform != "win32":
223+
poller.register(self.shutdown_watcher, selectors.EVENT_READ)
221224

222225
while True:
223-
poller.poll()
226+
poller.select()
224227
try:
225228
# If the socket is closed, this will raise an exception and exit
226-
# the loop. So we don't need to check the return value of poll().
229+
# the loop. So we don't need to check the return value of select().
227230
sock, addr = self.socket.accept()
228231
except OSError:
229232
break
@@ -236,7 +239,8 @@ def shutdown(self) -> None:
236239
237240
"""
238241
self.socket.close()
239-
os.write(self.shutdown_notifier, b"x")
242+
if sys.platform != "win32":
243+
os.write(self.shutdown_notifier, b"x")
240244

241245
def fileno(self) -> int:
242246
"""

0 commit comments

Comments
 (0)