aio-libs/aiozmq

client not terminating on connection timeout

mentaal opened this issue · 4 comments

I have a question raised here on SO: http://stackoverflow.com/questions/38767067/python-program-not-exiting-on-exception-using-aiozmq which another user could reproduce.

Basically I am using rpc.connect_rpc with a timeout value. Once the timeout has exceeded, an exception is raised but the running program doesn't terminate. Details can be found from the aforementioned question.

Here is a small example to reproduce this issue:

import asyncio
from aiozmq import rpc

async def client():
    client = await rpc.connect_rpc(
        connect='tcp://127.0.0.1:5555',
        timeout=1)
    return await client.call.some_function()

loop = asyncio.get_event_loop()
loop.run_until_complete(client())  

This program hangs after raising a TimeoutError, with two extra non-python threads running (zmq internal threads I would guess). Catching the TimeoutError and closing the loop doesn't help.

@vxgmichel Out of curiosity, how did you determine that there are two extra non-python threads still running? As a more general question (or perhaps we should leave this for SO) how does this prevent the interpreter from dying?

@mentaal I simply used htop:

 PID  PRI   NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
 5608 20   0  250M 20532  9592 S  0.4  0.5  0:00.24 │  │  └─ python3.5 test_aiozmq.py
 5610 20   0  250M 20532  9592 S  0.4  0.5  0:00.13 │  │     ├─ python3.5 test_aiozmq.py
 5609 20   0  250M 20532  9592 S  0.0  0.5  0:00.00 │  │     └─ python3.5 test_aiozmq.py

You already showed in your SO question that no extra python threads are running after the exception is raised.

I'm also stuck with this yesterday, after digging I found solution:
http://api.zeromq.org/2-1%3azmq-setsockopt#toc15

For proper work, code should look like:

import asyncio

import aiozmq.rpc
import zmq


async def foo():

    client = await aiozmq.rpc.connect_rpc(connect="tcp://127.0.0.1:5555",
                                          timeout=1)
    client.transport.setsockopt(zmq.LINGER, 0)
    return await client.call.test()

loop = asyncio.get_event_loop()
loop.run_until_complete(foo())

Don't really know is it too bad, but I vote for setting LINGER to 0 at initialization period, since 99% don't care about it. The main problem of this solution is that pyzmq behavior will differ from aiozmq behavior.