@@ -139,10 +139,6 @@ typedef struct {
139139 PyObject * asyncio_mod ;
140140 PyObject * context_kwname ;
141141
142- /* Dictionary containing tasks that are currently active in
143- all running event loops. {EventLoop: Task} */
144- PyObject * current_tasks ;
145-
146142 /* WeakSet containing scheduled 3rd party tasks which don't
147143 inherit from native asyncio.Task */
148144 PyObject * non_asyncio_tasks ;
@@ -2225,159 +2221,47 @@ unregister_eager_task(asyncio_state *state, PyObject *task)
22252221}
22262222
22272223static int
2228- enter_task (asyncio_state * state , PyObject * loop , PyObject * task )
2224+ enter_task (PyObject * loop , PyObject * task )
22292225{
2230- PyObject * item ;
2231- int res = PyDict_SetDefaultRef (state -> current_tasks , loop , task , & item );
2232- if (res < 0 ) {
2233- return -1 ;
2234- }
2235- else if (res == 1 ) {
2226+ _PyThreadStateImpl * ts = (_PyThreadStateImpl * )_PyThreadState_GET ();
2227+
2228+ if (ts -> asyncio_running_task != NULL ) {
22362229 PyErr_Format (
22372230 PyExc_RuntimeError ,
22382231 "Cannot enter into task %R while another " \
22392232 "task %R is being executed." ,
22402233 task , item , NULL );
2241- Py_DECREF (item );
22422234 return -1 ;
22432235 }
22442236
2245- assert (task == item );
2246- Py_CLEAR (item );
2247- set_ts_asyncio_running_task (loop , task );
2237+ ts -> asyncio_running_task = Py_NewRef (task );
22482238 return 0 ;
22492239}
22502240
22512241static int
2252- err_leave_task (PyObject * item , PyObject * task )
2253- {
2254- PyErr_Format (
2255- PyExc_RuntimeError ,
2256- "Leaving task %R does not match the current task %R." ,
2257- task , item );
2258- return -1 ;
2259- }
2260-
2261- static int
2262- leave_task_predicate (PyObject * item , void * task )
2263- {
2264- if (item != task ) {
2265- return err_leave_task (item , (PyObject * )task );
2266- }
2267- return 1 ;
2268- }
2269-
2270- static int
2271- leave_task (asyncio_state * state , PyObject * loop , PyObject * task )
2242+ leave_task (PyObject * loop , PyObject * task )
22722243{
2273- int res = _PyDict_DelItemIf (state -> current_tasks , loop ,
2274- leave_task_predicate , task );
2275- if (res == 0 ) {
2276- // task was not found
2277- return err_leave_task (Py_None , task );
2278- }
2279- clear_ts_asyncio_running_task (loop );
2280- return res ;
2281- }
2244+ _PyThreadStateImpl * ts = (_PyThreadStateImpl * )_PyThreadState_GET ();
22822245
2283- static PyObject *
2284- swap_current_task_lock_held (PyDictObject * current_tasks , PyObject * loop ,
2285- Py_hash_t hash , PyObject * task )
2286- {
2287- PyObject * prev_task ;
2288- if (_PyDict_GetItemRef_KnownHash_LockHeld (current_tasks , loop , hash , & prev_task ) < 0 ) {
2289- return NULL ;
2290- }
2291- if (_PyDict_SetItem_KnownHash_LockHeld (current_tasks , loop , task , hash ) < 0 ) {
2292- Py_XDECREF (prev_task );
2293- return NULL ;
2294- }
2295- if (prev_task == NULL ) {
2296- Py_RETURN_NONE ;
2246+ if (ts -> asyncio_running_task != task ) {
2247+ PyErr_Format (
2248+ PyExc_RuntimeError ,
2249+ "Cannot enter into task %R while another " \
2250+ "task %R is being executed." ,
2251+ task , item , NULL );
2252+ return -1 ;
22972253 }
2298- return prev_task ;
2254+ Py_CLEAR (ts -> asyncio_running_task );
2255+ return 0 ;
22992256}
23002257
23012258static PyObject *
2302- swap_current_task (asyncio_state * state , PyObject * loop , PyObject * task )
2259+ swap_current_task (PyObject * loop , PyObject * task )
23032260{
2304- PyObject * prev_task ;
2305-
2306- clear_ts_asyncio_running_task (loop );
2307- if (task == Py_None ) {
2308- if (PyDict_Pop (state -> current_tasks , loop , & prev_task ) < 0 ) {
2309- return NULL ;
2310- }
2311- if (prev_task == NULL ) {
2312- Py_RETURN_NONE ;
2313- }
2314- return prev_task ;
2315- }
2316-
2317- Py_hash_t hash = PyObject_Hash (loop );
2318- if (hash == -1 ) {
2319- return NULL ;
2320- }
2321-
2322- PyDictObject * current_tasks = (PyDictObject * )state -> current_tasks ;
2323- Py_BEGIN_CRITICAL_SECTION (current_tasks );
2324- prev_task = swap_current_task_lock_held (current_tasks , loop , hash , task );
2325- Py_END_CRITICAL_SECTION ();
2326- set_ts_asyncio_running_task (loop , task );
2327- return prev_task ;
2328- }
2329-
2330- static inline void
2331- set_ts_asyncio_running_task (PyObject * loop , PyObject * task )
2332- {
2333- // We want to enable debuggers and profilers to be able to quickly
2334- // introspect the asyncio running state from another process.
2335- // When we do that, we need to essentially traverse the address space
2336- // of a Python process and understand what every Python thread in it is
2337- // currently doing, mainly:
2338- //
2339- // * current frame
2340- // * current asyncio task
2341- //
2342- // A naive solution would be to require profilers and debuggers to
2343- // find the current task in the "_asynciomodule" module state, but
2344- // unfortunately that would require a lot of complicated remote
2345- // memory reads and logic, as Python's dict is a notoriously complex
2346- // and ever-changing data structure.
2347- //
2348- // So the easier solution is to put a strong reference to the currently
2349- // running `asyncio.Task` on the current thread state (the current loop
2350- // is also stored there.)
2351- _PyThreadStateImpl * ts = (_PyThreadStateImpl * )_PyThreadState_GET ();
2352- if (ts -> asyncio_running_loop == loop ) {
2353- // Protect from a situation when someone calls this method
2354- // from another thread. This shouldn't ever happen though,
2355- // as `enter_task` and `leave_task` can either be called by:
2356- //
2357- // - `asyncio.Task` itself, in `Task.__step()`. That method
2358- // can only be called by the event loop itself.
2359- //
2360- // - third-party Task "from scratch" implementations, that
2361- // our `capture_call_graph` API doesn't support anyway.
2362- //
2363- // That said, we still want to make sure we don't end up in
2364- // a broken state, so we check that we're in the correct thread
2365- // by comparing the *loop* argument to the event loop running
2366- // in the current thread. If they match we know we're in the
2367- // right thread, as asyncio event loops don't change threads.
2368- assert (ts -> asyncio_running_task == NULL );
2369- ts -> asyncio_running_task = Py_NewRef (task );
2370- }
2371- }
2372-
2373- static inline void
2374- clear_ts_asyncio_running_task (PyObject * loop )
2375- {
2376- // See comment in set_ts_asyncio_running_task() for details.
23772261 _PyThreadStateImpl * ts = (_PyThreadStateImpl * )_PyThreadState_GET ();
2378- if ( ts -> asyncio_running_loop == NULL || ts -> asyncio_running_loop == loop ) {
2379- Py_CLEAR ( ts -> asyncio_running_task );
2380- }
2262+ PyObject * prev_task = ts -> asyncio_running_task ;
2263+ ts -> asyncio_running_task = Py_NewRef ( task );
2264+ return prev_task ;
23812265}
23822266
23832267/* ----- Task */
@@ -3558,7 +3442,7 @@ static int
35583442task_eager_start (asyncio_state * state , TaskObj * task )
35593443{
35603444 assert (task != NULL );
3561- PyObject * prevtask = swap_current_task (state , task -> task_loop , (PyObject * )task );
3445+ PyObject * prevtask = swap_current_task (state , (PyObject * )task );
35623446 if (prevtask == NULL ) {
35633447 return -1 ;
35643448 }
@@ -3978,11 +3862,15 @@ _asyncio_current_task_impl(PyObject *module, PyObject *loop)
39783862 Py_INCREF (loop );
39793863 }
39803864
3981- int rc = PyDict_GetItemRef (state -> current_tasks , loop , & ret );
3982- Py_DECREF (loop );
3983- if (rc == 0 ) {
3984- Py_RETURN_NONE ;
3865+ _PyThreadStateImpl * ts = (_PyThreadStateImpl * )_PyThreadState_GET ();
3866+
3867+ if (ts -> asyncio_running_loop == loop ) {
3868+ ret = Py_XNewRef (ts -> asyncio_running_task );
3869+ Py_DECREF (loop );
3870+ return ret ;
39853871 }
3872+ _PyEval_StopTheWorld (ts -> base .interp );
3873+
39863874 return ret ;
39873875}
39883876
0 commit comments