Repository navigation
asyncio: support multiprocessing (support fork) #66285
Description
Activity
On non-Windows platforms, if a user attempts to use asyncio.get_event_loop() in a child process created by multiprocessing.Process using the fork context, and an asyncio event loop is also being used in the main process, the same _UnixSelectorEventLoop object will be used by both processes. This, of course, won't work properly; the child will raise a "RuntimeError: Event loop is running" exception as soon as it tries using the loop object.
However, this may or may not actually make it back to the parent: If the parent is expecting to get items from a queue from that child publishes to, rather than yielding from it immediately, the program will deadlock. Even if the child is yielded from, it may not be immediately obvious why "Event loop is running" was raised, and the behavior is inconsistent with the behavior if a method other than os.fork is used to create the child process, since the child will get a new event loop in that case.
So, it'd be better if _UnixDefaultEventLoopPolicy detected that get_event_loop was being called in a child process, and either
- Created a new loop for the child (this would make the behavior appear consistent no matter what platform/method for launching children is used)
- Raised an exception stating that no default event loop exists for this process, similar to the assert used for threads currently.
I've attached a test script that demonstrates the different between forked/spawned processes, and a patch that implements #1 above.
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Jul 26, 2014 - changed the title
[-]_UnixDefaultEventLoop policy should either create a new loop or explicilty fail when get_event_loop() is called from a multiprocessing child process[/-][+]_UnixDefaultEventLoopPolicy should either create a new loop or explicilty fail when get_event_loop() is called from a multiprocessing child process[/+]on Jul 26, 2014 Good point. Asyncio definitely should not share event loops across forked processes. However, I don't like the dependency on multiprocessing (even though it's in the stdlib) -- can't the policy just use os.getpid()?
Also, I've got a feeling that maybe the pid should be part of the policy state instead of the loop state? The policy could just reset self._local when the pid doesn't match.
Yep, agreed on both points. The latter suggestion also has the benefit of not requiring any test changes. Here's an updated patch.
I think there should still be a new unittest -- we're adding a behavior we're promising, so we should test it.
See aslo issue bpo-21998: "asyncio: a new self-pipe should be created in the child process after fork".
I've added a unit test that spawns a new forked process via multiprocessing, and verifies that the loop returned by get_event_loop is not the same as the one we have in the parent.
A simple pid check in the policy should be enough.
Hmm, I'm not sure what you mean. What check in the policy would prevent this issue you described in bpo-21998?:
import asyncio, os loop = asyncio.get_event_loop() pid = os.fork() if pid: print("parent", loop._csock.fileno(), loop._ssock.fileno()) else: print("child", loop._csock.fileno(), loop._ssock.fileno())
Output:
---
parent 6 5
child 6 5Are any other changes needed here? I'm still not completely clear on what Victor meant with his last comment.
This issue looks to be a duplicate of bpo-21998.
handle-mp_unix2.patch looks more to a workaround than a real issue. When I write asyncio code, I prefer to pass explicitly the loop, so get_event_loop() should never be called. IMO the methods of the event loop should detect the fork and handle the fork directly.
- changed the title
[-]_UnixDefaultEventLoopPolicy should either create a new loop or explicilty fail when get_event_loop() is called from a multiprocessing child process[/-][+]asyncio: support multiprocessing[/+]on Feb 4, 2015 32 remaining items
- added a commit that references this issue
on Nov 24, 2022 Let's keep this open until the buildbot results come in. Lukasz is bisecting them as we speak.
- added 4 commits that reference this issue
on Nov 24, 2022 - linked a pull request that will close this issueGH-66285: fix forking in asyncio #99769
on Nov 26, 2022 - added a commit that references this issue
on Nov 27, 2022 - added a commit that references this issue
on Dec 3, 2022 asyncio's event loop is typically initialized in the main process and is not designed to be inherited by child processes created via multiprocessing. am i right?
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
- StatusShow more project fieldsDone
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
asyncio#99539time.sleepfromtest_fork_signal_handling#99963