2

I am using Python 3.6.2, on Fedora 26 Workstation.

Below is some scrapbook code which demonstrates my issue:

EDIT: added Sam Hartman's suggestion to code.

import asyncio, json
from autobahn.asyncio.websocket import WebSocketClientProtocol, WebSocketClientFactory

class MyClientProtocol(WebSocketClientProtocol):

    def onConnect(self, response):
        print(response.peer)

    def onOpen(self):
        print("open")
        self.sendMessage(json.dumps({'command': 'subscribe', 'channel': "1010"}).encode("utf8"))

    def onMessage(self, payload, isBinary):
        print("message")
        print(json.loads(payload))

factory1 = WebSocketClientFactory("wss://api2.poloniex.com:443")
factory1.protocol = MyClientProtocol
loop1 = asyncio.get_event_loop()
loop1.run_until_complete(loop1.create_connection(factory1, "api2.poloniex.com", 443, ssl=True))

try:
    loop1.run_forever()
except KeyboardInterrupt:
    pass
loop1.close()

asyncio.set_event_loop(asyncio.new_event_loop())

factory2 = WebSocketClientFactory("wss://api2.poloniex.com:443")
factory2.protocol = MyClientProtocol
loop2 = asyncio.get_event_loop()
loop2.run_until_complete(loop2.create_connection(factory2, "api2.poloniex.com", 443, ssl=True))

try:
    loop2.run_forever()
except KeyboardInterrupt:
    pass
loop2.close()

After having closed an initial asyncio event loop, creating another and setting it as the global event loop, attempting to use the new event loop yields the following errors:

Fatal write error on socket transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x7f8a84ed4748>
transport: <_SelectorSocketTransport fd=6>
Traceback (most recent call last):
  File "/usr/lib64/python3.6/asyncio/selector_events.py", line 762, in write
    n = self._sock.send(data)
OSError: [Errno 9] Bad file descriptor
Fatal error on SSL transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x7f8a84ed4748>
transport: <_SelectorSocketTransport closing fd=6>
Traceback (most recent call last):
  File "/usr/lib64/python3.6/asyncio/selector_events.py", line 762, in write
    n = self._sock.send(data)
OSError: [Errno 9] Bad file descriptor

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib64/python3.6/asyncio/sslproto.py", line 648, in _process_write_backlog
    self._transport.write(chunk)
  File "/usr/lib64/python3.6/asyncio/selector_events.py", line 766, in write
    self._fatal_error(exc, 'Fatal write error on socket transport')
  File "/usr/lib64/python3.6/asyncio/selector_events.py", line 646, in _fatal_error
    self._force_close(exc)
  File "/usr/lib64/python3.6/asyncio/selector_events.py", line 658, in _force_close
    self._loop.call_soon(self._call_connection_lost, exc)
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 574, in call_soon
    self._check_closed()
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 357, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Exception in callback _SelectorSocketTransport._read_ready()
handle: <Handle _SelectorSocketTransport._read_ready()>
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/txaio/_common.py", line 63, in call_later
    self._buckets[real_time][1].append(call)
KeyError: 412835000

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib64/python3.6/asyncio/events.py", line 127, in _run
    self._callback(*self._args)
  File "/usr/lib64/python3.6/asyncio/selector_events.py", line 731, in _read_ready
    self._protocol.data_received(data)
  File "/usr/lib64/python3.6/asyncio/sslproto.py", line 503, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "/usr/lib64/python3.6/asyncio/sslproto.py", line 204, in feed_ssldata
    self._handshake_cb(None)
  File "/usr/lib64/python3.6/asyncio/sslproto.py", line 619, in _on_handshake_complete
    self._app_protocol.connection_made(self._app_transport)
  File "/usr/lib/python3.6/site-packages/autobahn/asyncio/websocket.py", line 97, in connection_made
    self._connectionMade()
  File "/usr/lib/python3.6/site-packages/autobahn/websocket/protocol.py", line 3340, in _connectionMade
    WebSocketProtocol._connectionMade(self)
  File "/usr/lib/python3.6/site-packages/autobahn/websocket/protocol.py", line 1055, in _connectionMade
    self.onOpenHandshakeTimeout,
  File "/usr/lib/python3.6/site-packages/txaio/_common.py", line 72, in call_later
    self._notify_bucket, real_time,
  File "/usr/lib/python3.6/site-packages/txaio/aio.py", line 382, in call_later
    return self._config.loop.call_later(delay, real_call)
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 543, in call_later
    timer = self.call_at(self.time() + delay, callback, *args)
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 553, in call_at
    self._check_closed()
  File "/usr/lib64/python3.6/asyncio/base_events.py", line 357, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

It seems reasonable that one might need to reopen an event loop after having closed an earlier one. Indeed this question even shows how: Asyncio Event Loop is Closed

The code below should achieve this:

loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

so I am clearly doing something wrong. Can somebody see something missing?

James Paul Turner
  • 791
  • 3
  • 8
  • 23

1 Answers1

1

I have fairly high confidence that your factory object is maintaining a reference to the old event loop presumably that it gets from asyncio.get_event_loop. Asyncio consumers are bad about getting hidden references to loops.

My recommendation is to reconstruct the web socket factory after closing the loop

Sam Hartman
  • 6,210
  • 3
  • 23
  • 40
  • Thanks Sam. I tried reconstructing the factory, even using different variable names for the first and second factory/loop, but I am still getting the same errors (`OSError: [Errno 9] Bad file descriptor`, `RuntimeError: Event loop is closed`, `KeyError: 15068000`). It's as if the `set_event_loop` call is simply not working. Do you think I should report this as a bug? – James Paul Turner Nov 04 '17 at 13:20
  • 1
    You can check that set_event_loop is working with an assert after that get_event_loop returns what you set it to. It's almost certainly dangling references somewhere, probably in your websocket implementation – Sam Hartman Nov 04 '17 at 14:44
  • OK. I'll ask the autobahn.asyncio folks. Thanks Sam. – James Paul Turner Nov 04 '17 at 15:45
  • Upstream bug report, for those who are interested: https://github.com/crossbario/autobahn-python/issues/925 – James Paul Turner Nov 05 '17 at 14:16