Skip to content

Commit

Permalink
protocol: add a lock around StreamWriter.drain(), closes #16
Browse files Browse the repository at this point in the history
Works around the following error:

    File /usr/lib/python3.4/asyncio/streams.py, line 194, in _drain_helper
      assert waiter is None or waiter.cancelled()
    AssertionError

when the write buffer reaches the high watermark (and thus blocks) and
2+ tasks try to call drain().  Clearly, asyncio's current code (all
versions up to 3.6.1 included) is not "thread" safe.  So use a lock
around the only place drain() is called.
  • Loading branch information
RemiCardona committed Mar 29, 2017
1 parent 2466e74 commit 198b715
Showing 1 changed file with 7 additions and 2 deletions.
9 changes: 7 additions & 2 deletions websockets/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def __init__(self, *,

self.reader = None
self.writer = None
self._drain_lock = asyncio.Lock(loop=loop)

self.request_headers = None
self.raw_request_headers = None
Expand Down Expand Up @@ -562,8 +563,12 @@ def write_frame(self, opcode, data=b''):
yield

try:
# Handle flow control automatically.
yield from self.writer.drain()
# drain() cannot be called concurrently by multiple coroutines:
# http://bugs.python.org/issue29930. Remove this lock when no
# version of Python where this bugs exists is supported anymore.
with (yield from self._drain_lock):
# Handle flow control automatically.
yield from self.writer.drain()
except ConnectionError:
# Terminate the connection if the socket died.
yield from self.fail_connection(1006)
Expand Down

0 comments on commit 198b715

Please sign in to comment.