Skip to content

asyncio: support multiprocessing (support fork) #66285

Description

@danoreilly
BPO 22087
Nosy @gvanrossum, @pitrou, @1st1, @thehesiod, @miss-islington
PRs
  • bpo-22087: Fix Policy.get_event_loop() to detect fork #7208
  • [3.7] bpo-22087: Fix Policy.get_event_loop() to detect fork (GH-7208) #7215
  • [3.6] bpo-22087: Fix Policy.get_event_loop() to detect fork (GH-7208) #7218
  • bpo-22087: Restructure code to be more robust and safe #7226
  • Revert "bpo-22087: Fix Policy.get_event_loop() to detect fork (GH-7208)" #7232
  • Revert "bpo-22087: Fix Policy.get_event_loop() to detect fork (GH-7208)" #7233
  • Files
  • test_loop.py: Test script demonstrating the issue
  • handle_mp_unix.diff: Patch that makes _UnixDefaultEventLoopPolicy create a new loop object if get_event_loop is called in a forked mp child process
  • handle-mp_unix2.patch: Use os.getpid() instead of multiprocessing. Store pid state in Policy instance rather than the Loop instance.
  • handle_mp_unix_with_test.diff: Adds a unit test to previous patch
  • 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:

    assignee = None
    closed_at = None
    created_at = <Date 2014-07-26.18:01:10.150>
    labels = ['type-bug', 'expert-asyncio']
    title = 'asyncio: support multiprocessing (support fork)'
    updated_at = <Date 2018-05-30.00:56:36.541>
    user = 'https://bugs.python.org/danoreilly'

    bugs.python.org fields:

    activity = <Date 2018-05-30.00:56:36.541>
    actor = 'yselivanov'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['asyncio']
    creation = <Date 2014-07-26.18:01:10.150>
    creator = 'dan.oreilly'
    dependencies = []
    files = ['36117', '36118', '36119', '36134']
    hgrepos = []
    issue_num = 22087
    keywords = ['patch']
    message_count = 23.0
    messages = ['224082', '224084', '224085', '224097', '224125', '224140', '224143', '224144', '224145', '226698', '235404', '235411', '288327', '297222', '297226', '297227', '297229', '318077', '318092', '318135', '318140', '318143', '318144']
    nosy_count = 7.0
    nosy_names = ['gvanrossum', 'pitrou', 'zmedico', 'yselivanov', 'thehesiod', 'dan.oreilly', 'miss-islington']
    pr_nums = ['7208', '7215', '7218', '7226', '7232', '7233']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue22087'
    versions = ['Python 3.4', 'Python 3.5', 'Python 3.6']

    Linked PRs

    Activity

    1. danoreilly commented on Jul 26, 2014

      danoreillymannequin
      MannequinAuthor

      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

      1. Created a new loop for the child (this would make the behavior appear consistent no matter what platform/method for launching children is used)
      2. 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.

    2. 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
    3. gvanrossum commented on Jul 26, 2014

      @gvanrossum
      Member

      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.

    4. danoreilly commented on Jul 26, 2014

      danoreillymannequin
      MannequinAuthor

      Yep, agreed on both points. The latter suggestion also has the benefit of not requiring any test changes. Here's an updated patch.

    5. gvanrossum commented on Jul 27, 2014

      @gvanrossum
      Member

      I think there should still be a new unittest -- we're adding a behavior we're promising, so we should test it.

    6. vstinner commented on Jul 27, 2014

      @vstinner
      Member

      See aslo issue bpo-21998: "asyncio: a new self-pipe should be created in the child process after fork".

    7. danoreilly commented on Jul 27, 2014

      danoreillymannequin
      MannequinAuthor

      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.

    8. danoreilly commented on Jul 27, 2014

      danoreillymannequin
      MannequinAuthor

      re: bpo-21998, perhaps it's time to revive bpo-16500? Without that, I'm not sure what can be done aside from documenting the need to call "loop = asyncio.get_event_loop()" in the child immediately after forking.

    9. vstinner commented on Jul 27, 2014

      @vstinner
      Member

      A simple pid check in the policy should be enough.

    10. danoreilly commented on Jul 27, 2014

      danoreillymannequin
      MannequinAuthor

      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 5

    11. danoreilly commented on Sep 10, 2014

      danoreillymannequin
      MannequinAuthor

      Are any other changes needed here? I'm still not completely clear on what Victor meant with his last comment.

    12. vstinner commented on Feb 4, 2015

      @vstinner
      Member

      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.

    13. 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
    14. 32 remaining items

    15. added a commit that references this issue on Nov 24, 2022
    16. Repository owner moved this from Done to In Progress in asyncioon Nov 24, 2022
    17. added a commit that references this issue on Nov 24, 2022
    18. gvanrossum commented on Nov 24, 2022

      @gvanrossum
      Member

      Let's keep this open until the buildbot results come in. Lukasz is bisecting them as we speak.

    19. added 4 commits that reference this issue on Nov 24, 2022
    20. linked a pull request that will close this issueGH-66285: fix forking in asyncio #99769on Nov 26, 2022
    21. Repository owner moved this from In Progress to Done in asyncioon Nov 27, 2022
    22. added a commit that references this issue on Nov 27, 2022
    23. added a commit that references this issue on Dec 3, 2022
    24. Sagor0078 commented on Oct 16, 2025

      @Sagor0078

      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?

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions