0

I'm going through Trio tutorial and I made an echo-client that sends message to an echo server for 10 seconds:

async def sender(client_stream, flag):
    print("sender: started!")
    end_time = time.time() + 10
    while time.time() < end_time:
        data = b"async can sometimes be confusing, but I believe in you!"
        print("sender: sending {!r}".format(data))
        await client_stream.send_all(data)
        await trio.sleep(0)
    flag = False
    print("Left the while 10 seconds loops")

and wait for responses while the flag is `True.

async def receiver(client_stream, flag):
    print("receiver: started!")
    while(flag):
        data = await client_stream.receive_some()
        print("receiver: got data {!r}".format(data))
    print("receiver: connection closed")
    sys.exit()

The problem is that sometimes the program hangs at line data = await client_stream.receive_some() because of concurrency issues with regards to the variable flag.

How do I send a signal from the sender co-routine to the receiver co-routine?

Here is the entire program that you can run.

Jenia Ivanov
  • 1,849
  • 1
  • 25
  • 49

1 Answers1

1

It doesn't just sometimes hang there, it hangs all the time because the flag variable in receiver() never gets changed. I think you're under the impression that it's somehow shared between receiver() and sender(). It's not.

The simplest way you could fix that is by passing it in a container:

async def sender(client_stream, flag):
    print("sender: started!")
    end_time = time.time() + 10
    while time.time() < end_time:
        data = b"async can sometimes be confusing, but I believe in you!"
        print("sender: sending {!r}".format(data))
        await client_stream.send_all(data)
        await trio.sleep(0)
    flag[0] = False
    print("Left the while 10 seconds loops")

async def receiver(client_stream, flag):
    print("receiver: started!")
    while flag[0]:
        data = await client_stream.receive_some()
        print("receiver: got data {!r}".format(data))
    print("receiver: connection closed")
    sys.exit()

async def start_server():
    print("parent: connecting to 127.0.0.1:{}".format(PORT))
    client_stream = await trio.open_tcp_stream("127.0.0.1", PORT)
    flag = [False]
    async with client_stream:
        async with trio.open_nursery() as nursery:
            print("parent: spawning sender...")
            nursery.start_soon(sender, client_stream, flag)

            print("parent: spawning receiver...")
            nursery.start_soon(receiver, client_stream, flag)

A more elegant solution would be closing the stream in sender() and catching ClosedResourceError in receiver():

async def sender(client_stream):
    print("sender: started!")
    data = b"async can sometimes be confusing, but I believe in you!"
    with trio.move_on_after(10):
        print("sender: sending {!r}".format(data))
        await client_stream.send_all(data)

    await client_stream.aclose()
    print("Left the while 10 seconds loops")

async def receiver(client_stream):
    print("receiver: started!")
    try:
        async for data in client_stream:
            print("receiver: got data {!r}".format(data))
    except trio.ClosedResourceError:
        print("receiver: connection closed")

Notice how you don't even need sys.exit() for the program to end.

Alex Grönholm
  • 4,109
  • 20
  • 28