Skip to content

Commit 0eb7df7

Browse files
work
1 parent 09fe550 commit 0eb7df7

File tree

1 file changed

+29
-141
lines changed

1 file changed

+29
-141
lines changed

Modules/_asynciomodule.c

Lines changed: 29 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -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

22272223
static 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

22512241
static 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

23012258
static 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
35583442
task_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

Comments
 (0)