CabbageDevelopment / qasync

Python library for using asyncio in Qt-based applications.
BSD 2-Clause "Simplified" License
307 stars 43 forks source link

Differences between qasync.QEventLoop.close and asyncio.BaseEventLoop.close #115

Open kormax opened 3 months ago

kormax commented 3 months ago

When investigating the source code of qasync/asyncio while working on #113, I found out about the following difference in behaviour when the loop closes.

asyncio.BaseEventLoop.close does not wait for its concurrent.futures.ThreadPoolExecutor to shut down:

if executor is not None:
    self._default_executor = None
    executor.shutdown(wait=False)

qasync.QEventLoop.close waits for its qasync.QThreadExecutor to shut down (because argument wait=True by default)

if self.__default_executor is not None:
    self.__default_executor.shutdown()

Was this intended?

I could see issues happening if an app closes while some thread was in the middle of operation, so this could serve as an intended safeguard.

On the other Hand, this behaviour causes the app to hang on exit if there's a long-lasting blocking function running in executor (uncontrolled by developer, invoked by other library, for instance getaddrinfo which hangs if a domain is internal, known to device, but currently unavailable).

An additional thing to consider, is that in newer python versions, a "fix" was added to concurrent.futures.ThreadPoolExecutor that prevents an interpreter from fully shutting down until all ThreadPoolExecutor child threads complete. But it's using an atexit hook that joins those threads.

Another thing to consider, is that in newer python versions, a "fix" was added to concurrent.futures.ThreadPoolExecutor that prevents an interpreter from fully shutting down until all ThreadPoolExecutor child threads complete. But it's using an atexit hook that joins those threads. Doing it similarly would require a more drastic change to the code.