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

asyncio: stop using get_event_loop(). introduce ~singleton loop. #7792

Merged
merged 2 commits into from
May 4, 2022

Conversation

SomberNight
Copy link
Member

asyncio.get_event_loop() became deprecated in python3.10. (see python/cpython#83710)

.../electrum/electrum/daemon.py:470: DeprecationWarning: There is no current event loop
  self.asyncio_loop = asyncio.get_event_loop()
.../electrum/electrum/network.py:276: DeprecationWarning: There is no current event loop
  self.asyncio_loop = asyncio.get_event_loop()

Also, according to that thread, "set_event_loop() [... is] not deprecated by oversight".
So, we stop using get_event_loop() and set_event_loop() in our own code.
Note that libraries we use (such as the stdlib for python <3.10), might call get_event_loop,
which then relies on us having called set_event_loop e.g. for the GUI thread. To work around
this, a custom event loop policy providing a get_event_loop implementation is used.

Previously, we have been using a single asyncio event loop, created with
util.create_and_start_event_loop, and code in many places got a reference to this loop
using asyncio.get_event_loop().
Now, we still use a single asyncio event loop, but it is now stored as a global in
util._asyncio_event_loop (access with util.get_asyncio_loop()).

I believe these changes also fix #5376

asyncio.get_event_loop() became deprecated in python3.10. (see python/cpython#83710)
```
.../electrum/electrum/daemon.py:470: DeprecationWarning: There is no current event loop
  self.asyncio_loop = asyncio.get_event_loop()
.../electrum/electrum/network.py:276: DeprecationWarning: There is no current event loop
  self.asyncio_loop = asyncio.get_event_loop()
```
Also, according to that thread, "set_event_loop() [... is] not deprecated by oversight".
So, we stop using get_event_loop() and set_event_loop() in our own code.
Note that libraries we use (such as the stdlib for python <3.10), might call get_event_loop,
which then relies on us having called set_event_loop e.g. for the GUI thread. To work around
this, a custom event loop policy providing a get_event_loop implementation is used.

Previously, we have been using a single asyncio event loop, created with
util.create_and_start_event_loop, and code in many places got a reference to this loop
using asyncio.get_event_loop().
Now, we still use a single asyncio event loop, but it is now stored as a global in
util._asyncio_event_loop (access with util.get_asyncio_loop()).

I believe these changes also fix spesmilo#5376
@SomberNight SomberNight force-pushed the 202204_asyncio_cleanups branch from b6a14a2 to 8ba58b8 Compare April 29, 2022 16:49
@ecdsa
Copy link
Member

ecdsa commented Apr 30, 2022

great work
note: create_and_start_event_loop no longer needs to be imported from a few files.

self.electrum_path = tempfile.mkdtemp()

def tearDown(self):
self.asyncio_loop.call_soon_threadsafe(self._stop_loop.set_result, 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is super().tearDown() not called first?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although in many cases the order does not matter, here it might, depending on the unit test framework running the tests I guess: SequentialTestCase.setUp() and SequentialTestCase.tearDown() take and release self.test_lock, which ensure that tests do not run in parallel. If super().tearDown() is called before we stop the asyncio loop, and the test framework supports running tests in parallel, some tests might fail when trying to create an "overlapping" event loop.

The natural order of calling destructors is the reverse of constructors: Create parent first, child next, but destroy child first, parent next (btw this is the order in C++, where the calls happen implicitly). Code in the child might be relying on code in the parent, so the parent shouldn't be destroyed first. I guess we could clean up the rest of the tearDown implementations to respect this.

@SomberNight SomberNight force-pushed the 202204_asyncio_cleanups branch from 8ba58b8 to 872ce82 Compare May 3, 2022 23:53
@SomberNight
Copy link
Member Author

note: create_and_start_event_loop no longer needs to be imported from a few files.

removed.

@ecdsa ecdsa merged commit 6a9df7d into spesmilo:master May 4, 2022
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

Successfully merging this pull request may close these issues.

RuntimeError: There is no current event loop in thread
2 participants