Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stream.write() prevents Python process from terminating after loop clean up using task cancellation #61

Closed
achimnol opened this issue Oct 24, 2015 · 4 comments

Comments

@achimnol
Copy link
Member

write() calls to an aiozmq stream prevent "clean termination" of the Python process.
Here is a minimal working example:

#! /usr/bin/env python3
import asyncio, aiozmq, zmq

@asyncio.coroutine
def test(loop):
    # connect to a non-existent endpoint (this returns immediately)
    sock = yield from aiozmq.create_zmq_stream(zmq.REQ, connect='tcp://localhost:9999', loop=loop)
    sock.write([b'test'])
    # just make some time to press Ctrl+C
    yield from asyncio.sleep(5)
    # below is not executed as sleep is cancelled.
    _ = yield from sock.read()
    sock.close()

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    asyncio.async(test(loop))
    try: 
        loop.run_forever()
    except KeyboardInterrupt:
        for t in asyncio.Task.all_tasks():
            if not t.done():
                t.cancel()  # cancels the sleep call
        loop.run_until_complete(asyncio.sleep(0))
    finally:
        loop.close()
        print('exit')

The result when you press Ctrl+C during sleep is:

$ python3 test-aiozmq-write-cancel.py
^Cexit   # Python hangs here; If I comment the write() call, it goes normal.
^C       # I need to press Ctrl+C again to terminate the Python process completely.

Is there any way to correctly interrupt/cancel the background behavior of write() calls?

@achimnol achimnol changed the title stream.write() makes termination using task cancellation to hang stream.write() prevents Python process from terminating after loop clean up using task cancellation Oct 24, 2015
@achimnol
Copy link
Member Author

Using gdb, I found what is blocking after my code has finished.

(gdb) bt
#0  0x00007ffff71d712d in poll () at ../sysdeps/unix/syscall-template.S:81
#1  0x00007ffff3700a6a in ?? () from /usr/lib/x86_64-linux-gnu/libzmq.so.3
#2  0x00007ffff36ed2f7 in ?? () from /usr/lib/x86_64-linux-gnu/libzmq.so.3
#3  0x00007ffff36e463c in ?? () from /usr/lib/x86_64-linux-gnu/libzmq.so.3
#4  0x00007ffff3715388 in zmq_ctx_term () from /usr/lib/x86_64-linux-gnu/libzmq.so.3
#5  0x00007ffff294c7e9 in __pyx_f_3zmq_7backend_6cython_7context_7Context__term (__pyx_v_self=0x7ffff24b5f28) at zmq/backend/cython/context.c:1773
#6  __pyx_pf_3zmq_7backend_6cython_7context_7Context_4__dealloc__ (__pyx_v_self=0x7ffff24b5f28) at zmq/backend/cython/context.c:1391
#7  __pyx_pw_3zmq_7backend_6cython_7context_7Context_5__dealloc__ (__pyx_v_self=0x7ffff24b5f28) at zmq/backend/cython/context.c:1323
#8  __pyx_tp_dealloc_3zmq_7backend_6cython_7context_Context (o=0x7ffff24b5f28) at zmq/backend/cython/context.c:3093
#9  0x0000000000487176 in subtype_dealloc (self=0x7ffff24b5f28) at Objects/typeobject.c:1226
#10 0x000000000046914f in free_keys_object (keys=0xc0f5f0) at Objects/dictobject.c:354
#11 PyDict_Clear (op=<optimized out>) at Objects/dictobject.c:1323
#12 0x000000000046b532 in PyDict_Clear (op=<optimized out>) at Objects/dictobject.c:1301
#13 0x000000000048b9d8 in type_clear (type=0xc0b638) at Objects/typeobject.c:3282
#14 0x0000000000542992 in delete_garbage (old=<optimized out>, collectable=<optimized out>) at Modules/gcmodule.c:866
#15 collect (generation=generation@entry=2, n_collected=n_collected@entry=0x0, n_uncollectable=n_uncollectable@entry=0x0, nofail=nofail@entry=1) at Modules/gcmodule.c:1014
#16 0x00000000005435b1 in _PyGC_CollectNoFail () at Modules/gcmodule.c:1605
#17 0x000000000050e33c in PyImport_Cleanup () at Python/import.c:481
#18 0x0000000000521de9 in Py_Finalize () at Python/pylifecycle.c:576
#19 0x0000000000521fb5 in Py_Finalize () at Python/pylifecycle.c:524
#20 0x00000000004204d7 in Py_Main (argc=argc@entry=2, argv=argv@entry=0x8ee010) at Modules/main.c:788
#21 0x000000000041c3b7 in main (argc=2, argv=<optimized out>) at ./Programs/python.c:69

@asvetlov
Copy link
Member

Hmm. Looks interesting.
Do you have hang on zmq context termination stage?
Not sure what can I change in aiozmq but you may try zmq.Context.instance().term() after loop.close() call.

@achimnol
Copy link
Member Author

Oh, I just googled a bit and experimented another bit, and found that setting LINGER option resolves this problem.
e.g., Insertsock.transport.setsockopt(zmq.LINGER, 50) just after the create_zmq_stream() call.

It seems that the underlying TCP connection was not established (because I'm using a wrong address intentionally) and the followed write call makes the termination process of libzmq to poll availability of the TCP connection internally.

@achimnol
Copy link
Member Author

Therefore, this is not the "fault" of aiozmq at all; I'm closing the issue. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants