Why doesn't this async code stop?


Why doesn't this async code stop?



The following code snippet has two coroutines each for server and client. The client coroutine has a logic to break the while loop after 10 seconds and server should stop after 15 seconds.


while



When I run the script this doesn't stop, ideally, it should stop after 15 seconds but this is not happening.


import asyncio
import time
import zmq
import zmq.asyncio

zmq.asyncio.install()

ctx = zmq.asyncio.Context()


server_socket = ctx.socket(zmq.REP)
client_socket = ctx.socket(zmq.REQ)

server_socket.bind("tcp://127.0.0.1:8899")
client_socket.connect("tcp://127.0.0.1:8899")

t0 = time.time()


@asyncio.coroutine
def server_coroutine():

while True:
msg = yield from server_socket.recv_string()
print(msg)
msg = "Server:: {}".format(msg)
yield from server_socket.send_string(msg)
t1 = time.time()
elapsed_time = t1 - t0
# print('elapsed time is {}'.format(elapsed_time))
if elapsed_time > 15:
print("Breaking Server loop")
break

@asyncio.coroutine
def client_coroutine():
counter = 0
while True:
yield from asyncio.sleep(2)
msg = 'Message: {}'.format(counter)
yield from client_socket.send_string(msg)
res = yield from client_socket.recv_string()
print(res)
t1 = time.time()
elapsed_time = t1 - t0
print('elapsed time is {}'.format(elapsed_time))
if elapsed_time > 10:
print("Breaking Client loop")
break
counter += 1


if __name__ == '__main__':

loop = asyncio.get_event_loop()

loop.run_until_complete(asyncio.gather(
asyncio.ensure_future(server_coroutine()),
asyncio.ensure_future(client_coroutine())
))




1 Answer
1



If you run code you will see something like this:


Server:: Message: 4
elapsed time is 10.022311687469482
Breaking Client loop



ok, client_coroutine finished successfully, but what state of server_coroutine at this moment? It stuck at this line msg = yield from server_socket.recv_string() waiting for possibility to recive string from server_socket, but it won't happen since there's already no client to send it! And since your event loop runs until both coroutines done it would run forever.


client_coroutine


server_coroutine


msg = yield from server_socket.recv_string()


server_socket



Here's the simplest fix:


@asyncio.coroutine
def server_coroutine():
while True:
msg = yield from server_socket.recv_string()
if msg == 'CLOSE': # LOOK HERE 1
break
print(msg)
msg = "Server:: {}".format(msg)
yield from server_socket.send_string(msg)
t1 = time.time()
elapsed_time = t1 - t0
# print('elapsed time is {}'.format(elapsed_time))
if elapsed_time > 15:
print("Breaking Server loop")
break

@asyncio.coroutine
def client_coroutine():
counter = 0
while True:
yield from asyncio.sleep(2)
msg = 'Message: {}'.format(counter)
yield from client_socket.send_string(msg)
res = yield from client_socket.recv_string()
print(res)
t1 = time.time()
elapsed_time = t1 - t0
print('elapsed time is {}'.format(elapsed_time))
if elapsed_time > 10:
print("Breaking Client loop")
yield from client_socket.send_string('CLOSE') # LOOK HERE 2
break
counter += 1



Note, this fix is only to demonstrate issue and one possible way to solve it.



In real life I think you would want to do something different: probably, set timeouts to you coroutines to guarantee they won't stuck forever if client/server stops responding.





Thanks, Mikhail is there a way for a large application with many coroutine, where event-loop is waiting/ stuck?
– bruce_wayne
Dec 30 '17 at 1:14





@bruce_wayne you should always have timeouts for your operations. In asyncio it's not a big problem: timeouted task can be cancelled with standard mechanism. Most convenient way to achieve it is to use asyncio.wait_for coroutine or nice third-party async-timeout context manager (you can place multiple operations inside it).
– Mikhail Gerasimov
Dec 30 '17 at 9:40






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

How to input without newline? (Python)

C++ thread error: no type named ‘type’ MINGW

Analog for TagView in flutter