Event loop is not stopped after calling stop() method using python asyncio module

Question:

I have written the following code:

import asyncio
import time
from threading import Thread
from threading import Timer

async def myCouroutine():
    time.sleep(3)

dbAccessLoop = None

def stopEventLoop():
    dbAccessLoop.stop()

def eventLoopThreadMain():
    global dbAccessLoop
    try:
        dbAccessLoop = asyncio.new_event_loop()
        dbAccessLoop.run_forever()
    finally:
        dbAccessLoop.close()

def main():
    thread = Thread(target = eventLoopThreadMain)
    thread.start()

    stopLoopTimer = Timer(3, stopEventLoop)
    stopLoopTimer.start()

    while dbAccessLoop is not None and dbAccessLoop.is_running():
        dbAccessLoop.call_soon_threadsafe(asyncio.ensure_future, myCouroutine())
        break

    thread.join()

if __name__ == '__main__':
    main()

The program creates new event loop, starts it, runs coroutine (doing some work) and then after some time using timer it stops the loop. I expect that after calling stop() method dbAccessLoop.run_forever() will give execution back, but that is not happening.

Do I missed something here? My guess is that stop() method is not thread safe but I do not see any errors in the console. Thanks in advance.

Asked By: Oleg

||

Answers:

There seems to be a hole at least in the setting of the dbAccessLoop.

main -> thread: start
main -> while: dbAccessLoop is None => bailout
thread -> dbAccessLoop = whatever

Apart from that, if thread gets to set dbAccessLoop, it will spawn a tremendous amount of async jobs in the loop, each one waiting to be finished.

It is very well possible that the job queue is just so full of 3-second waits that the stop, even if it were thread safe, only gets executed after 3155123412 seconds.

Also, https://docs.python.org/3/library/asyncio-dev.html#detect-never-awaited-coroutines tells me that the coroutine myCoroutine might not be scheduled. But that’s another problem.

And apart from that, indeed "Almost all asyncio objects are not thread safe, which is typically not a problem unless there is code that works with them from outside of a Task or a callback." (https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading), so the code should call the stop in the eventloop’s thread using dbAccessLoop.call_soon_threadsafe(dbAccessLoop.stop).

Answered By: xtofl