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

gh-121621: Move asyncio running loop to thread state #121695

Merged
merged 21 commits into from
Jul 16, 2024

Conversation

Fidget-Spinner
Copy link
Member

@Fidget-Spinner Fidget-Spinner commented Jul 13, 2024

@ericsnowcurrently
Copy link
Member

CC @markshannon

Copy link
Contributor

@colesbury colesbury left a comment

Choose a reason for hiding this comment

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

I think it might be worthwhile to split this up into two PRs:

  • The running loop change, which should be small and relatively straightforward
  • The freelist change, which I think needs more work to avoid duplicating struct definitions

Include/cpython/pystate.h Outdated Show resolved Hide resolved
Modules/_asynciomodule.c Outdated Show resolved Hide resolved
@@ -3816,12 +3814,15 @@ module_traverse(PyObject *mod, visitproc visit, void *arg)
Py_VISIT(state->context_kwname);

// Visit freelist.
PyObject *next = (PyObject*) state->fi_freelist;
#ifdef WITH_FREELISTS
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem right. We don't visit freelists anywhere else. The objects in the freelist should never be tracked by the GC.

@@ -147,6 +156,39 @@ extern void _PyAsyncGen_ClearFreeLists(struct _Py_object_freelists *freelists, i
extern void _PyContext_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization);
extern void _PyObjectStackChunk_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization);

// Keep in sync with _asynciomodule.c !
typedef struct futureiterobject_dummy {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we want to avoid this sort of duplication if at all possible.

@Fidget-Spinner Fidget-Spinner changed the title gh-121621: Move asyncio freelist to thread state gh-121621: Move asyncio running loop to thread state Jul 15, 2024
Copy link
Contributor

@colesbury colesbury left a comment

Choose a reason for hiding this comment

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

A few suggestions:

I think the get_running_loop and set_running_loop are more confusing than helpful at this point. For example, there's a bunch of dead code now to access the module state. I think the code will be easier to read if they are inlined into their callsites.

I think the code will be more robust if the loop field on PyThreadState uses NULL instead of Py_None (i.e., handle the conversion to Py_None in the call sites as necessary). PyThreadState_Clear() still sets the field to NULL, which breaks the non-NULL invariant -- PyThreadState_Clear can still execute Python code via finalizers.

Include/cpython/pystate.h Outdated Show resolved Hide resolved
@Fidget-Spinner
Copy link
Member Author

A few suggestions:

I think the get_running_loop and set_running_loop are more confusing than helpful at this point. For example, there's a bunch of dead code now to access the module state. I think the code will be easier to read if they are inlined into their callsites.

I think the code will be more robust if the loop field on PyThreadState uses NULL instead of Py_None (i.e., handle the conversion to Py_None in the call sites as necessary). PyThreadState_Clear() still sets the field to NULL, which breaks the non-NULL invariant -- PyThreadState_Clear can still execute Python code via finalizers.

I agree with the first suggestion.

The second suggestion has one problem --- we'd still need to special case Py_None because the asyncio spec says if the event loop is None, it will raise a RuntimeError. So we'd need an extra branch checking for NULL and converting to Py_None, AND a branch checking if it's non-null but Py_None. That's not clean IMO.

I'd prefer we just set it to Py_None in PyThreadState_Clear to keep the invariant that there are only two states: Py_None and not-none.

Copy link
Contributor

@colesbury colesbury left a comment

Choose a reason for hiding this comment

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

This LGTM. I left a few minor style comments.

...we'd still need to special case Py_None because the asyncio spec says if the event loop is None, it will raise a RuntimeError.

This is a common pattern in CPython. We normalize Py_None to NULL in the setter. For example, _asyncio__set_running_loop would look like:

PyThreadState *ts = _PyThreadState_GET();
if (loop == Py_None) {
    loop = NULL;
}
Py_XSETREF(ts->asyncio_running_loop, Py_XNewRef(loop));

For example, see:

if (value == Py_None)
value = NULL;

This is a much more common pattern than setting the field to Py_None in the clear function.

Modules/_asynciomodule.c Outdated Show resolved Hide resolved
Modules/_asynciomodule.c Outdated Show resolved Hide resolved
Modules/_asynciomodule.c Outdated Show resolved Hide resolved
@Fidget-Spinner Fidget-Spinner merged commit 69c68de into python:main Jul 16, 2024
34 checks passed
@Fidget-Spinner Fidget-Spinner added the needs backport to 3.13 bugs and security fixes label Jul 16, 2024
@miss-islington-app
Copy link

Thanks @Fidget-Spinner for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Jul 16, 2024
…121695)

(cherry picked from commit 69c68de)

Co-authored-by: Ken Jin <kenjin@python.org>
@bedevere-app
Copy link

bedevere-app bot commented Jul 16, 2024

GH-121864 is a backport of this pull request to the 3.13 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.13 bugs and security fixes label Jul 16, 2024
@Fidget-Spinner Fidget-Spinner deleted the asyncio_rest_threadsafe branch July 16, 2024 17:10
Fidget-Spinner added a commit that referenced this pull request Jul 16, 2024
… (GH-121864)

gh-121621: Move asyncio running loop to thread state (GH-121695)
(cherry picked from commit 69c68de)

Co-authored-by: Ken Jin <kenjin@python.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants