Skip to content

Commit 031ec31

Browse files
committed
Prevent close() from blocking when reading is paused.
Fix #1555.
1 parent 6317c00 commit 031ec31

File tree

4 files changed

+26
-6
lines changed

4 files changed

+26
-6
lines changed

docs/project/changelog.rst

+7-4
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,18 @@ notice.
3535
Bug fixes
3636
.........
3737

38-
* Fixed ``connection.recv(timeout=0)`` in the :mod:`threading` implementation.
39-
If a message is already received, it is returned. Previously,
40-
:exc:`TimeoutError` was raised incorrectly.
41-
4238
* Wrapped errors when reading the opening handshake request or response in
4339
:exc:`~exceptions.InvalidMessage` so that :func:`~asyncio.client.connect`
4440
raises :exc:`~exceptions.InvalidHandshake` or a subclass when the opening
4541
handshake fails.
4642

43+
* Fixed :meth:`~sync.connection.Connection.recv` with ``timeout=0`` in the
44+
:mod:`threading` implementation. If a message is already received, it is
45+
returned. Previously, :exc:`TimeoutError` was raised incorrectly.
46+
47+
* Prevented :meth:`~sync.connection.Connection.close` from blocking when
48+
receive buffers are saturated in the :mod:`threading` implementation.
49+
4750
.. _14.1:
4851

4952
14.1

src/websockets/asyncio/messages.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ def close(self) -> None:
283283
"""
284284
End the stream of frames.
285285
286-
Callling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`,
286+
Calling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`,
287287
or :meth:`put` is safe. They will raise :exc:`EOFError`.
288288
289289
"""

src/websockets/sync/messages.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def close(self) -> None:
298298
"""
299299
End the stream of frames.
300300
301-
Callling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`,
301+
Calling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`,
302302
or :meth:`put` is safe. They will raise :exc:`EOFError`.
303303
304304
"""
@@ -311,3 +311,8 @@ def close(self) -> None:
311311
if self.get_in_progress:
312312
# Unblock get() or get_iter().
313313
self.frames.put(None)
314+
315+
if self.paused:
316+
# Unblock recv_events().
317+
self.paused = False
318+
self.resume()

tests/sync/test_messages.py

+12
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,18 @@ def test_put_fails_after_close(self):
496496
with self.assertRaises(EOFError):
497497
self.assembler.put(Frame(OP_TEXT, b"caf\xc3\xa9"))
498498

499+
def test_close_resumes_reading(self):
500+
"""close unblocks reading when queue is above the high-water mark."""
501+
self.assembler.put(Frame(OP_TEXT, b"caf\xc3\xa9"))
502+
self.assembler.put(Frame(OP_TEXT, b"more caf\xc3\xa9"))
503+
self.assembler.put(Frame(OP_TEXT, b"water"))
504+
505+
# queue is at the high-water mark
506+
assert self.assembler.paused
507+
508+
self.assembler.close()
509+
self.resume.assert_called_once_with()
510+
499511
def test_close_is_idempotent(self):
500512
"""close can be called multiple times safely."""
501513
self.assembler.close()

0 commit comments

Comments
 (0)