autobahn-python icon indicating copy to clipboard operation
autobahn-python copied to clipboard

WebSocketClientFactory: unable to create new event loop

Open jamesturner246 opened this issue 6 years ago • 3 comments

Hi all.

I am using Fedora 26 Workstation, with Python 3.6.2 and the asyncio backend of autobahn | python 17.9.3.

I have an issue where I am unable to create and use a new event loop after having closed another one using:

factory = WebSocketClientFactory("<URL>:443")
factory.protocol = MyClientProtocol
loop.run_until_complete(loop2.create_connection(factory, "<URL>", 443, ssl=True))

Here is a full code example, which demonstrates the issue:

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()

Running the example, the first event loop executes correctly, and I get a connection. After Ctrl-C and running loop2.run_until_complete(loop2.create_connection( ... )) I get the following errors:

Fatal write error on socket transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x7fe57419c7f0>
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 0x7fe57419c7f0>
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: 28880000

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

I am able to renew the event loop in the following simpler asyncio example:

import asyncio

async def hello_world():
    print("Hello World!")

coro1 = hello_world()
loop1 = asyncio.get_event_loop()
loop1.run_until_complete(coro1)    # print "Hello World!"
loop1.close()

asyncio.set_event_loop(asyncio.new_event_loop())

coro2 = hello_world()
loop2 = asyncio.get_event_loop()
loop2.run_until_complete(coro2)    # print "Hello World!"
loop2.close()

assert(loop1 != loop2)

But not when using autobahn.asyncio WebSocketClientFactory.

A StackOverflow user has suggested that the problem might be dangling pointers in the Websocket implementaion (thread: https://stackoverflow.com/questions/47101290/asyncio-unable-to-create-new-event-loop), which leads me here. I am happy to provide further information and do more testing, but some guidance would be helpful.

All the best, James.

jamesturner246 avatar Nov 05 '17 14:11 jamesturner246

asyncio.get_event_loop() gives you always the same global event loop. The close method (see documentation)

is idempotent and irreversible. No other methods should be called after this one.

Probably what you want is to do

loop2 = asyncio.new_event_loop()

etam avatar Mar 29 '18 09:03 etam

It should work .. with asyncio.new_event_loop() ..

the problem might be dangling pointers in the Websocket implementaion

Ok, I see. needs investigation.

oberstet avatar Mar 29 '18 13:03 oberstet

Have you solution?

pisulski avatar Dec 21 '19 23:12 pisulski