GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Audit asyncio thread safety · Issue #128002 · python/cpython

Audit asyncio thread safety  #128002

New issue
Copy link
New issue
Copy link
Closed
#134324
Closed
Audit asyncio thread safety #128002
#134324
Copy link
Labels
topic-asynciotopic-free-threading

Description

@kumaraditya303
kumaraditya303
opened on Dec 16, 2024
Issue body actions

The following functions needs to be made thread safe and atomic for free-threading for both pure python and C implementation:

  • asyncio._enter_task
  • asyncio._leave_task
  • asyncio._register_task
  • asyncio._unregister_task
  • asyncio._swap_current_task
  • asyncio.current_task
  • asyncio.all_tasks

Note that some of these were made thread safe in C impl #120974

The following classes needs to be thread safe for free-threading in C implementation:

  • asyncio.Task
  • asyncio.Future

Both of these classes are documented to be not thread safe but currently calling methods on these classes from multiple threads can crash the interpreter. The pure python implementation cannot crash the interpreter when called from multiple threads so changes are only needed for C implementation. Before making these thread safe in C I would gather some numbers for how much a difference the C implementation makes in free-threading and if it isn't much we can just disable the C extension for free-threading.

cc @colesbury

Linked PRs

  • gh-128002: fix many thread safety issues in asyncio #128147
  • gh-128002: use internal llist implementation for asyncio tasks  #128256
  • gh-128002: add more thread safety tests for asyncio #128480
  • gh-128002: fix asyncio.all_tasks against concurrent deallocations of tasks #128541
  • gh-128002: use per threads tasks linked list in asyncio #128869
  • gh-128002: use _PyObject_SetMaybeWeakref when creating tasks in asyncio #128885
  • gh-128002: fix test_all_tasks_different_thread in asyncio #129267
  • gh-128002: simplify all_tasks to use PyList_Extend instead of manual iteration #129942
  • gh-128002: add fast path for native tasks in asyncio.all_tasks #129943
  • gh-128002: optimistically remove tasks from linked list when finished  #129995
  • gh-128002: use efficient linked list impl for eager tasks in asyncio #130518
  • gh-128002: add test_asyncio.test_free_threading to tsan tests #131106
  • gh-128002: fix tsan race in closing socket in test_ssl #131397
  • gh-128002: fix tsan race in closing socket in test_sslproto #131399
  • gh-128002: run all asyncio tests with tsan CI #131797
  • [3.14] gh-128002: add what's new docs for asyncio (GH-134324) #134362
  • gh-128002: add what's new docs for asyncio  #134324
Reacted by Sam Gross, Andrew Svetlov, Erlend E. Aasland, Łukasz Langa and J. Nick Koston
Reacted by EDM115

Activity

Next
  1. kumaraditya303
    added
    topic-asyncio
    topic-free-threading
    on Dec 16, 2024
  2. github-project-automation
    added this to asyncioon Dec 16, 2024
  3. github-project-automation
    moved this to Todo in asyncioon Dec 16, 2024
  4. colesbury commented on Dec 16, 2024

    @colesbury
    colesbury
    on Dec 16, 2024
    Contributor
    More actions

    You may be already planning on this, but I think it'd be helpful to have a summary of the current thread safety issues in these classes and functions.

  5. kumaraditya303 commented on Dec 18, 2024

    @kumaraditya303
    kumaraditya303
    on Dec 18, 2024
    ContributorAuthor
    More actions

    I have gathered data of performance of pure python implementation and C implementation.

    Details

    Without free-threading

    Benchmark C implementation Pure Python implementation
    async_tree_cpu_io_mixed_tg 478 ms 746 ms: 1.56x slower
    async_tree_io_tg 620 ms 1.00 sec: 1.61x slower
    async_tree_cpu_io_mixed 493 ms 796 ms: 1.62x slower
    async_tree_io 631 ms 1.03 sec: 1.63x slower
    async_tree_memoization_tg 314 ms 580 ms: 1.85x slower
    async_tree_memoization 338 ms 632 ms: 1.87x slower
    async_tree_none 272 ms 522 ms: 1.92x slower
    async_tree_none_tg 252 ms 489 ms: 1.94x slower
    Geometric mean (ref) 1.50x slower

    With free-threading

    Benchmark C implementation Pure Python implementation
    async_tree_cpu_io_mixed_tg 580 ms 951 ms: 1.64x slower
    async_tree_cpu_io_mixed 627 ms 1.03 sec: 1.65x slower
    async_tree_io_tg 765 ms 1.28 sec: 1.67x slower
    async_tree_io 810 ms 1.37 sec: 1.69x slower
    async_tree_memoization_tg 433 ms 805 ms: 1.86x slower
    async_tree_memoization 472 ms 884 ms: 1.87x slower
    async_tree_none 372 ms 733 ms: 1.97x slower
    async_tree_none_tg 333 ms 679 ms: 2.04x slower
    Geometric mean (ref) 1.53x slower

    From this data, it looks like there's on average slowdown of 1.50x when disabling the C implementation as such it is too large of performance loss to consider dropping the C implementation even in case of free-threading.
    I was hoping that the difference would be less so we could disable the C implementation for free-threading and reduce the changes but it isn't worth the perf loss.

    Some thread safety Issues in C implementation

    Here's an outline of some of the thread safety issues, I'll be adding more to this list.

    • Critical sections needs to be added methods of asyncio.Task and asyncio.Future otherwise they can crash the interpreter in C implementation. Even though tasks are documented to be not thread safe but still it shouldn't crash the interpreter. Example: tasks could be concurrently cancelled while it's stepping through the coroutine in another thread. The cancellation counter isn't atomic so it could lead to possible loss of cancellations requests.

      cpython/Modules/_asynciomodule.c

      Lines 2363 to 2373 in bad3cde

      _asyncio_Task_uncancel_impl(TaskObj *self)
      /*[clinic end generated code: output=58184d236a817d3c input=68f81a4b90b46be2]*/
      /*[clinic end generated code]*/
      {
      if (self->task_num_cancels_requested > 0) {
      self->task_num_cancels_requested -= 1;
      if (self->task_num_cancels_requested == 0) {
      self->task_must_cancel = 0;
      }
      }
      return PyLong_FromLong(self->task_num_cancels_requested);

    • The eager_tasks set could be concurrently mutated while it's being iterated over in another thread in asyncio.all_tasks.

      cpython/Modules/_asynciomodule.c

      Lines 3606 to 3608 in bad3cde

      // First add eager tasks to the set so that we don't miss
      // any tasks which graduates from eager to non-eager
      PyObject *eager_iter = PyObject_GetIter(state->eager_tasks);

    • asyncio.Handle needs to be thread safe because it can be called from any thread with loop.call_soon_threadsafe.

    • The internal asyncio linked list uses borrowed references to task as such can be concurrently deallocated while list is being read in another thread as deallocation doesn't hold the state lock.

    Performance bottlenecks in free-threading

    Consider an async server which uses multiple threads to run one independent loop per thread.

    • asyncio currently relies on globals dicts and sets like current_tasks, eager_tasks, non_asyncio_tasks etc which would perform poorly with free-threading. Moving these to per thread would help in performance scaling.
    • Use non python data structures, this was an overall performance win when I implemented double linked implementation because we can it avoids a lot of reference counting at expense of some locking which could possibly be further optimized to compare exchanges.
    • maybe defer reference counting on the loop objects which are the source of most reference counting operations.
  6. itamaro commented on Dec 18, 2024

    @itamaro
    itamaro
    on Dec 18, 2024
    Contributor
    More actions
    • asyncio currently relies on globals dicts and sets like current_tasks, eager_tasks, non_asyncio_tasks etc which would perform poorly with free-threading. Moving these to per thread would help in performance scaling.

    I vaguely remember past discussions about moving the global state to live on the loop. I don't remember what was the conclusion (or if there was a conclusion), but if we do that, that would resolve most thread-safety issues, wouldn't it?

  7. kumaraditya303 commented on Dec 18, 2024

    @kumaraditya303
    kumaraditya303
    on Dec 18, 2024
    ContributorAuthor
    More actions

    I vaguely remember past discussions about moving the global state to live on the loop. I don't remember what was the conclusion (or if there was a conclusion), but if we do that, that would resolve most thread-safety issues, wouldn't it?

    That has backwards compatibility issues with custom event loops, currently asyncio handles it for all loops. Moving things to event loop may improve performance for free-threading but will causes performance loss in normal builds and in cases where just a single loop runs because of extra attribute lookups and indirection and we cannot use non-python data structures like the new double linked-lists implementation.

  8. kumaraditya303
    added a commit that references this issue on Dec 25, 2024

    gh-128002: use internal llist implementation for asyncio tasks (#128256)

    76f1785
  9. kumaraditya303
    added a commit that references this issue on Jan 4, 2025

    gh-128002: fix many thread safety issues in asyncio (#128147)

    513a4ef
  10. WolframAlph
    added a commit that references this issue on Jan 4, 2025

    pythongh-128002: fix many thread safety issues in asyncio (python#128147

    bdfe2ca
  11. srinivasreddy
    added 2 commits that reference this issue on Jan 8, 2025

    pythongh-128002: use internal llist implementation for asyncio tasks (p…

    f956778

    pythongh-128002: fix many thread safety issues in asyncio (python#128147

    4a1dabe
  12. kumaraditya303
    added 2 commits that reference this issue on Jan 9, 2025

    gh-128002: fix `asyncio.all_tasks` against concurrent deallocations o…

    7dc41ad

    gh-128002: add more thread safety tests for asyncio (#128480)

    3efe28a
  13. 19 remaining items

  14. kumaraditya303
    added a commit that references this issue on Mar 18, 2025

    gh-128002: fix tsan race in closing socket in `test_sslproto` (#131399)

    ab6333f
  15. colesbury
    added 2 commits that reference this issue on Mar 20, 2025

    pythongh-128002: fix tsan race in closing socket in `test_ssl` (pytho…

    339ce45

    pythongh-128002: fix tsan race in closing socket in `test_sslproto` (p…

    7fb91c1
  16. kumaraditya303
    added a commit that references this issue on Mar 28, 2025

    gh-128002: run all asyncio tests with tsan CI (#131797)

    9752661
  17. kumaraditya303
    closed this as completedin #134324on May 20, 2025
  18. kumaraditya303
    added a commit that references this issue on May 20, 2025

    gh-128002: add what's new docs for `asyncio` (#134324)

    0584533
  19. github-project-automation
    moved this from Todo to Done in asyncioon May 20, 2025
  20. miss-islington
    added a commit that references this issue on May 20, 2025

    pythongh-128002: add what's new docs for `asyncio` (pythonGH-134324)

    26ac03e
  21. kumaraditya303
    added a commit that references this issue on May 20, 2025

    [3.14] gh-128002: add what's new docs for `asyncio` (GH-134324) (#134362

    402d983
  22. lkollar
    added a commit that references this issue on May 26, 2025

    pythongh-128002: add what's new docs for `asyncio` (python#134324)

    0a2f01d
  23. Pranjal095
    added a commit that references this issue on Jul 12, 2025

    pythongh-128002: add what's new docs for `asyncio` (python#134324)

    901883f
  24. taegyunkim
    added a commit that references this issue on Aug 4, 2025

    pythongh-128002: add what's new docs for `asyncio` (python#134324)

    b6a335c
  25. copybara-service
    added a commit that references this issue on Jun 11, 2026

    [Python] Remove use of asyncio.all_tasks on channel close to support …

    7269456
  26. grohman-hub
    added a commit that references this issue on Jun 16, 2026

    [Python] Remove use of asyncio.all_tasks on channel close to support …

    07efaf9
  27. asheshvidyut
    added a commit that references this issue on Nov 12, 2026

    [Python] Remove use of asyncio.all_tasks on channel close to support …

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic-asynciotopic-free-threading

    Projects

    • asyncio
      Status
      Done
      Show more project fields

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    You are visitor 01402503 — GeezerVison does not store the pages you look up.
    Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.