From 8a1c13d57d451bd4e96a0c2c395fae94fb4140ab Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 15 Jan 2025 15:30:30 +0000 Subject: [PATCH 1/4] use _PyObject_SetMaybeWeakref --- Modules/_asynciomodule.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 48f0ef95934fa4..36af5be0dbcd7b 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3,6 +3,7 @@ #endif #include "Python.h" +#include "pycore_object.h" // _PyObject_SetMaybeWeakref #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_MUT() #include "pycore_dict.h" // _PyDict_GetItem_KnownHash() #include "pycore_freelist.h" // _Py_FREELIST_POP() @@ -2226,6 +2227,11 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, if (task_call_step_soon(state, self, NULL)) { return -1; } +#ifdef Py_GIL_DISABLED + // This is required so that _Py_TryIncref(self) + // works correctly in non-owning threads. + _PyObject_SetMaybeWeakref((PyObject *)self); +#endif register_task(state, self); return 0; } From 51f092dc86d03889df3112e48ea51fb92286fd50 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Wed, 15 Jan 2025 15:33:54 +0000 Subject: [PATCH 2/4] sort headers --- Modules/_asynciomodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 36af5be0dbcd7b..b6857b21151d55 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -3,13 +3,13 @@ #endif #include "Python.h" -#include "pycore_object.h" // _PyObject_SetMaybeWeakref #include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION_MUT() #include "pycore_dict.h" // _PyDict_GetItem_KnownHash() #include "pycore_freelist.h" // _Py_FREELIST_POP() #include "pycore_llist.h" // struct llist_node #include "pycore_modsupport.h" // _PyArg_CheckPositional() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_object.h" // _PyObject_SetMaybeWeakref #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() #include "pycore_pystate.h" // _PyThreadState_GET() From 3add9e4ad906416e044942307ab2eda3cd932c43 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 23 Jan 2025 19:50:05 +0000 Subject: [PATCH 3/4] add test --- Lib/test/test_asyncio/test_free_threading.py | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Lib/test/test_asyncio/test_free_threading.py b/Lib/test/test_asyncio/test_free_threading.py index 8f4bba5f3b97d9..c8b22b47dd9f75 100644 --- a/Lib/test/test_asyncio/test_free_threading.py +++ b/Lib/test/test_asyncio/test_free_threading.py @@ -1,4 +1,5 @@ import asyncio +import threading import unittest from threading import Thread from unittest import TestCase @@ -58,6 +59,38 @@ def runner(): with threading_helper.start_threads(threads): pass + def test_all_tasks_different_thread(self) -> None: + loop = None + started = threading.Event() + + async def coro(): + await asyncio.sleep(0.01) + + lock = threading.Lock() + count = 0 + + async def main(): + nonlocal count, loop + loop = asyncio.get_running_loop() + started.set() + for i in range(1000): + with lock: + asyncio.create_task(coro()) + count = len(self.all_tasks(loop)) + + runner = threading.Thread(target=lambda: asyncio.run(main())) + + def check(): + started.wait() + with lock: + self.assertEqual(count, len(self.all_tasks(loop))) + + threads = [threading.Thread(target=check) for _ in range(10)] + threads.append(runner) + + with threading_helper.start_threads(threads): + pass + def test_run_coroutine_threadsafe(self) -> None: results = [] From ede0c4945e9202354722af6f41ed7ec10e7d6f9c Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Fri, 24 Jan 2025 15:09:24 +0000 Subject: [PATCH 4/4] use a less strict test --- Lib/test/test_asyncio/test_free_threading.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_asyncio/test_free_threading.py b/Lib/test/test_asyncio/test_free_threading.py index f7fbc7fced3ad0..c91719cb577c2f 100644 --- a/Lib/test/test_asyncio/test_free_threading.py +++ b/Lib/test/test_asyncio/test_free_threading.py @@ -67,23 +67,23 @@ async def coro(): await asyncio.sleep(0.01) lock = threading.Lock() - count = 0 + tasks = set() async def main(): - nonlocal count, loop + nonlocal tasks, loop loop = asyncio.get_running_loop() started.set() for i in range(1000): with lock: asyncio.create_task(coro()) - count = len(self.all_tasks(loop)) + tasks = self.all_tasks(loop) runner = threading.Thread(target=lambda: asyncio.run(main())) def check(): started.wait() with lock: - self.assertEqual(count, len(self.all_tasks(loop))) + self.assertSetEqual(tasks & self.all_tasks(loop), tasks) threads = [threading.Thread(target=check) for _ in range(10)] threads.append(runner)