diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 28a76c36801b4b..a3563401e23fa7 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -737,6 +737,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_abc_impl)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_abstract_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_active)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_add_pending_task)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_anonymous_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_argtypes_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_as_parameter_)); @@ -745,11 +746,13 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_bootstrap)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_check_retval_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_dealloc_warn)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_del_pending_task)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_feature_version)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_field_types)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_fields_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_finalizing)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_find_and_load)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_finish_execution)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_fix_up_module)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_flags_)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(_get_sourcefile)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index ac789b06fb8a61..f40d51a9097638 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -226,6 +226,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(_abc_impl) STRUCT_FOR_ID(_abstract_) STRUCT_FOR_ID(_active) + STRUCT_FOR_ID(_add_pending_task) STRUCT_FOR_ID(_anonymous_) STRUCT_FOR_ID(_argtypes_) STRUCT_FOR_ID(_as_parameter_) @@ -234,11 +235,13 @@ struct _Py_global_strings { STRUCT_FOR_ID(_bootstrap) STRUCT_FOR_ID(_check_retval_) STRUCT_FOR_ID(_dealloc_warn) + STRUCT_FOR_ID(_del_pending_task) STRUCT_FOR_ID(_feature_version) STRUCT_FOR_ID(_field_types) STRUCT_FOR_ID(_fields_) STRUCT_FOR_ID(_finalizing) STRUCT_FOR_ID(_find_and_load) + STRUCT_FOR_ID(_finish_execution) STRUCT_FOR_ID(_fix_up_module) STRUCT_FOR_ID(_flags_) STRUCT_FOR_ID(_get_sourcefile) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 7847a5c63ebf3f..c450e5f7f9a751 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -735,6 +735,7 @@ extern "C" { INIT_ID(_abc_impl), \ INIT_ID(_abstract_), \ INIT_ID(_active), \ + INIT_ID(_add_pending_task), \ INIT_ID(_anonymous_), \ INIT_ID(_argtypes_), \ INIT_ID(_as_parameter_), \ @@ -743,11 +744,13 @@ extern "C" { INIT_ID(_bootstrap), \ INIT_ID(_check_retval_), \ INIT_ID(_dealloc_warn), \ + INIT_ID(_del_pending_task), \ INIT_ID(_feature_version), \ INIT_ID(_field_types), \ INIT_ID(_fields_), \ INIT_ID(_finalizing), \ INIT_ID(_find_and_load), \ + INIT_ID(_finish_execution), \ INIT_ID(_fix_up_module), \ INIT_ID(_flags_), \ INIT_ID(_get_sourcefile), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index a688f70a2ba36f..543b3efb80b969 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -704,6 +704,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(_add_pending_task); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(_anonymous_); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -736,6 +740,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(_del_pending_task); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(_feature_version); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); @@ -756,6 +764,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); assert(PyUnicode_GET_LENGTH(string) != 1); + string = &_Py_ID(_finish_execution); + _PyUnicode_InternStatic(interp, &string); + assert(_PyUnicode_CheckConsistency(string, 1)); + assert(PyUnicode_GET_LENGTH(string) != 1); string = &_Py_ID(_fix_up_module); _PyUnicode_InternStatic(interp, &string); assert(_PyUnicode_CheckConsistency(string, 1)); diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index ffcc0174e1e245..63aa4efd852240 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -449,6 +449,10 @@ def __init__(self): # Set to True when `loop.shutdown_default_executor` is called. self._executor_shutdown_called = False + # Holds references to all pending tasks to avoid garbage + # collection (see #91887) + self._pending_tasks = set() + def __repr__(self): return ( f'<{self.__class__.__name__} running={self.is_running()} ' @@ -2042,6 +2046,16 @@ def _set_coroutine_origin_tracking(self, enabled): self._coroutine_origin_tracking_enabled = enabled + def _add_pending_task(self, task): + """Add task to the _pending_tasks set. + + This avoids garbage collection as long as the loop is alive. + """ + self._pending_tasks.add(task) + + def _del_pending_task(self, task): + self._pending_tasks.discard(task) + def get_debug(self): return self._debug diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py index 5f6fa2348726cf..efefc6ab22e3ae 100644 --- a/Lib/asyncio/futures.py +++ b/Lib/asyncio/futures.py @@ -151,11 +151,11 @@ def cancel(self, msg=None): return False self._state = _CANCELLED self._cancel_message = msg - self.__schedule_callbacks() + self._finish_execution() return True - def __schedule_callbacks(self): - """Internal: Ask the event loop to call all callbacks. + def _finish_execution(self): + """Ask the event loop to call all callbacks. The callbacks are scheduled to be called as soon as possible. Also clears the callback list. @@ -256,7 +256,7 @@ def set_result(self, result): raise exceptions.InvalidStateError(f'{self._state}: {self!r}') self._result = result self._state = _FINISHED - self.__schedule_callbacks() + self._finish_execution() def set_exception(self, exception): """Mark the future done and set an exception. @@ -278,7 +278,7 @@ def set_exception(self, exception): self._exception = exception self._exception_tb = exception.__traceback__ self._state = _FINISHED - self.__schedule_callbacks() + self._finish_execution() self.__log_traceback = True def __await__(self): diff --git a/Lib/asyncio/tasks.py b/Lib/asyncio/tasks.py index 2112dd4b99d17f..52c2df122b2120 100644 --- a/Lib/asyncio/tasks.py +++ b/Lib/asyncio/tasks.py @@ -112,6 +112,10 @@ def __init__(self, coro, *, loop=None, name=None, context=None, self._loop.call_soon(self.__step, context=self._context) _register_task(self) + def _finish_execution(self): + super()._finish_execution() + _unregister_task(self) + def __del__(self): if self._state == futures._PENDING and self._log_destroy_pending: context = { @@ -1032,6 +1036,8 @@ def factory(loop, coro, *, name=None, context=None): def _register_task(task): """Register an asyncio Task scheduled to run on an event loop.""" _scheduled_tasks.add(task) + loop = futures._get_loop(task) + loop._add_pending_task(task) def _register_eager_task(task): @@ -1067,6 +1073,8 @@ def _swap_current_task(loop, task): def _unregister_task(task): """Unregister a completed, scheduled Task.""" _scheduled_tasks.discard(task) + loop = futures._get_loop(task) + loop._del_pending_task(task) def _unregister_eager_task(task): diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index a1013ab803348d..b8061c62acf294 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2283,6 +2283,17 @@ async def kill_me(loop): source_traceback = task._source_traceback task = None + # no more reference to kill_me() task in user code. The task + # should be kept alive as long as it is pending and we hold a + # reference to the event loop (#91887) + support.gc_collect() + + self.assertEqual(len(self.all_tasks(loop=self.loop)), 1) + mock_handler.assert_not_called() + + # remove strong reference held by the event loop + self.loop._pending_tasks.clear() + # no more reference to kill_me() task: the task is destroyed by the GC support.gc_collect() diff --git a/Misc/ACKS b/Misc/ACKS index b2529601a2f71a..2c55e4e0d1bcfb 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -724,6 +724,7 @@ Derek Harland Jason Harper David Harrigan Brian Harring +Alexander Hartl Jonathan Hartley Travis B. Hartwell Henrik Harutyunyan diff --git a/Misc/NEWS.d/next/Library/2024-07-02-14-07-32.gh-issue-91887.eWzc5E.rst b/Misc/NEWS.d/next/Library/2024-07-02-14-07-32.gh-issue-91887.eWzc5E.rst new file mode 100644 index 00000000000000..4564f4d5b577a0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-07-02-14-07-32.gh-issue-91887.eWzc5E.rst @@ -0,0 +1,2 @@ +Retain strong references for pending :class:`asyncio.Task` objects to avoid rare crashes due +to background tasks. Patch by Alexander Hartl. diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 870084100a1b85..1d134d80c495b6 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -458,6 +458,17 @@ future_schedule_callbacks(asyncio_state *state, FutureObj *fut) return 0; } +static inline int +future_finish_execution(FutureObj *fut) +{ + PyObject *res = PyObject_CallMethodNoArgs((PyObject *)fut, + &_Py_ID(_finish_execution)); + if (res == NULL) { + return -1; + } + Py_DECREF(res); + return 0; +} static int future_init(FutureObj *fut, PyObject *loop) @@ -534,7 +545,7 @@ future_set_result(asyncio_state *state, FutureObj *fut, PyObject *res) fut->fut_result = Py_NewRef(res); fut->fut_state = STATE_FINISHED; - if (future_schedule_callbacks(state, fut) == -1) { + if (future_finish_execution(fut) < 0) { return NULL; } Py_RETURN_NONE; @@ -598,7 +609,7 @@ future_set_exception(asyncio_state *state, FutureObj *fut, PyObject *exc) fut->fut_exception_tb = PyException_GetTraceback(exc_val); fut->fut_state = STATE_FINISHED; - if (future_schedule_callbacks(state, fut) == -1) { + if (future_finish_execution(fut) < 0) { return NULL; } @@ -756,10 +767,9 @@ future_cancel(asyncio_state *state, FutureObj *fut, PyObject *msg) Py_XINCREF(msg); Py_XSETREF(fut->fut_cancel_msg, msg); - if (future_schedule_callbacks(state, fut) == -1) { + if (future_finish_execution(fut) < 0) { return NULL; } - Py_RETURN_TRUE; } @@ -1416,6 +1426,26 @@ _asyncio_Future__make_cancelled_error_impl(FutureObj *self) return create_cancelled_error(state, self); } +/*[clinic input] +_asyncio.Future._finish_execution + +Ask the event loop to call all callbacks. + +The callbacks are scheduled to be called as soon as possible. Also +clears the callback list. +[clinic start generated code]*/ + +static PyObject * +_asyncio_Future__finish_execution_impl(FutureObj *self) +/*[clinic end generated code: output=77354407293553bb input=c18b4c42a7810aa9]*/ +{ + asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); + if (future_schedule_callbacks(state, self) == -1) { + return NULL; + } + Py_RETURN_NONE; +} + static void FutureObj_finalize(FutureObj *fut) { @@ -1487,6 +1517,7 @@ static PyMethodDef FutureType_methods[] = { _ASYNCIO_FUTURE_DONE_METHODDEF _ASYNCIO_FUTURE_GET_LOOP_METHODDEF _ASYNCIO_FUTURE__MAKE_CANCELLED_ERROR_METHODDEF + _ASYNCIO_FUTURE__FINISH_EXECUTION_METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* Sentinel */ }; @@ -1917,21 +1948,59 @@ static PyMethodDef TaskWakeupDef = { /* ----- Task introspection helpers */ static void -register_task(asyncio_state *state, TaskObj *task) +register_task(asyncio_state *state, PyObject *pytask) { + PyObject *res; + ASYNCIO_STATE_LOCK(state); - assert(Task_Check(state, task)); - assert(task != &state->asyncio_tasks.tail); - if (task->next != NULL) { - // already registered - goto exit; - } - assert(task->prev == NULL); - assert(state->asyncio_tasks.head != NULL); - - task->next = state->asyncio_tasks.head; - state->asyncio_tasks.head->prev = task; - state->asyncio_tasks.head = task; + if (Task_Check(state, pytask)) { + // If task is an asyncio.Task instance or subclass, use efficient + // linked-list implementation. + TaskObj *task = (TaskObj *) pytask; + assert(task != &state->asyncio_tasks.tail); + if (task->next != NULL) { + // already registered + goto exit; + } + assert(task->prev == NULL); + assert(state->asyncio_tasks.head != NULL); + + task->next = state->asyncio_tasks.head; + state->asyncio_tasks.head->prev = task; + state->asyncio_tasks.head = task; + + // Ensure there is a persistent reference to the pending task + res = PyObject_CallMethodOneArg(task->task_loop, + &_Py_ID(_add_pending_task), pytask); + if (res == NULL) { + goto exit; + } + Py_DECREF(res); + } else { + // As task does not inherit from asyncio.Task, fallback to less efficient + // weakset implementation. + res = PyObject_CallMethodOneArg(state->non_asyncio_tasks, &_Py_ID(add), pytask); + if (res == NULL) { + goto exit; + } + Py_DECREF(res); + + // Ensure there is a persistent reference to the pending task + // Get a reference to the task loop + PyObject *task_loop = get_future_loop(state, pytask); + if (task_loop == NULL) { + goto exit; + } + + res = PyObject_CallMethodOneArg(task_loop, + &_Py_ID(_add_pending_task), pytask); + Py_DECREF(task_loop); + if (res == NULL) { + goto exit; + } + Py_DECREF(res); + } + exit: ASYNCIO_STATE_UNLOCK(state); } @@ -1943,27 +2012,62 @@ register_eager_task(asyncio_state *state, PyObject *task) } static void -unregister_task(asyncio_state *state, TaskObj *task) +unregister_task(asyncio_state *state, PyObject *pytask) { + PyObject *res; + ASYNCIO_STATE_LOCK(state); - assert(Task_Check(state, task)); - assert(task != &state->asyncio_tasks.tail); - if (task->next == NULL) { - // not registered - assert(task->prev == NULL); + if (Task_Check(state, pytask)) { + TaskObj *task = (TaskObj *) pytask; + + assert(task != &state->asyncio_tasks.tail); + if (task->next == NULL) { + // not registered + assert(task->prev == NULL); + assert(state->asyncio_tasks.head != task); + goto exit; + } + task->next->prev = task->prev; + if (task->prev == NULL) { + assert(state->asyncio_tasks.head == task); + state->asyncio_tasks.head = task->next; + } else { + task->prev->next = task->next; + } + task->next = NULL; + task->prev = NULL; assert(state->asyncio_tasks.head != task); - goto exit; - } - task->next->prev = task->prev; - if (task->prev == NULL) { - assert(state->asyncio_tasks.head == task); - state->asyncio_tasks.head = task->next; + + // Delete the persistent reference to the pending task + res = PyObject_CallMethodOneArg(task->task_loop, + &_Py_ID(_del_pending_task), pytask); + if (res == NULL) { + goto exit; + } + Py_DECREF(res); } else { - task->prev->next = task->next; + res = PyObject_CallMethodOneArg(state->non_asyncio_tasks, + &_Py_ID(discard), pytask); + if (res == NULL) { + goto exit; + } + Py_DECREF(res); + + // Delete the persistent reference to the pending task + // Get a reference to the task loop + PyObject *task_loop = get_future_loop(state, pytask); + if (task_loop == NULL) { + goto exit; + } + + res = PyObject_CallMethodOneArg(task_loop, &_Py_ID(_del_pending_task), pytask); + Py_DECREF(task_loop); + if (res == NULL) { + goto exit; + } + Py_DECREF(res); } - task->next = NULL; - task->prev = NULL; - assert(state->asyncio_tasks.head != task); + exit: ASYNCIO_STATE_UNLOCK(state); } @@ -2164,7 +2268,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, if (task_call_step_soon(state, self, NULL)) { return -1; } - register_task(state, self); + register_task(state, (PyObject *)self); return 0; } @@ -2574,6 +2678,25 @@ _asyncio_Task_set_name(TaskObj *self, PyObject *value) Py_RETURN_NONE; } + + +/*[clinic input] +_asyncio.Task._finish_execution +[clinic start generated code]*/ + +static PyObject * +_asyncio_Task__finish_execution_impl(TaskObj *self) +/*[clinic end generated code: output=9d218be34fb814b7 input=783199407feedc8a]*/ +{ + asyncio_state *state = get_asyncio_state_by_def((PyObject *)self); + if (future_schedule_callbacks(state, (FutureObj *)self) < 0) { + return NULL; + } + unregister_task(state, (PyObject*)self); + Py_RETURN_NONE; +} + + static void TaskObj_finalize(TaskObj *task) { @@ -2584,7 +2707,7 @@ TaskObj_finalize(TaskObj *task) // should use the asyncio._unregister_task function. // See https://docs.python.org/3/library/asyncio-extending.html#task-lifetime-support - unregister_task(state, task); + unregister_task(state, (PyObject *)task); PyObject *context; PyObject *message = NULL; @@ -2665,6 +2788,7 @@ static PyMethodDef TaskType_methods[] = { _ASYNCIO_TASK_SET_NAME_METHODDEF _ASYNCIO_TASK_GET_CORO_METHODDEF _ASYNCIO_TASK_GET_CONTEXT_METHODDEF + _ASYNCIO_TASK__FINISH_EXECUTION_METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* Sentinel */ }; @@ -3197,7 +3321,7 @@ task_eager_start(asyncio_state *state, TaskObj *task) } if (task->task_state == STATE_PENDING) { - register_task(state, task); + register_task(state, (PyObject *)task); } else { // This seems to really help performance on pyperformance benchmarks Py_CLEAR(task->task_coro); @@ -3360,20 +3484,7 @@ _asyncio__register_task_impl(PyObject *module, PyObject *task) /*[clinic end generated code: output=8672dadd69a7d4e2 input=21075aaea14dfbad]*/ { asyncio_state *state = get_asyncio_state(module); - if (Task_Check(state, task)) { - // task is an asyncio.Task instance or subclass, use efficient - // linked-list implementation. - register_task(state, (TaskObj *)task); - Py_RETURN_NONE; - } - // As task does not inherit from asyncio.Task, fallback to less efficient - // weakset implementation. - PyObject *res = PyObject_CallMethodOneArg(state->non_asyncio_tasks, - &_Py_ID(add), task); - if (res == NULL) { - return NULL; - } - Py_DECREF(res); + register_task(state, task); Py_RETURN_NONE; } @@ -3414,16 +3525,7 @@ _asyncio__unregister_task_impl(PyObject *module, PyObject *task) /*[clinic end generated code: output=6e5585706d568a46 input=28fb98c3975f7bdc]*/ { asyncio_state *state = get_asyncio_state(module); - if (Task_Check(state, task)) { - unregister_task(state, (TaskObj *)task); - Py_RETURN_NONE; - } - PyObject *res = PyObject_CallMethodOneArg(state->non_asyncio_tasks, - &_Py_ID(discard), task); - if (res == NULL) { - return NULL; - } - Py_DECREF(res); + unregister_task(state, task); Py_RETURN_NONE; } diff --git a/Modules/clinic/_asynciomodule.c.h b/Modules/clinic/_asynciomodule.c.h index d619a124ccead5..6fbfa68048cf27 100644 --- a/Modules/clinic/_asynciomodule.c.h +++ b/Modules/clinic/_asynciomodule.c.h @@ -481,6 +481,27 @@ _asyncio_Future__make_cancelled_error(FutureObj *self, PyObject *Py_UNUSED(ignor return _asyncio_Future__make_cancelled_error_impl(self); } +PyDoc_STRVAR(_asyncio_Future__finish_execution__doc__, +"_finish_execution($self, /)\n" +"--\n" +"\n" +"Ask the event loop to call all callbacks.\n" +"\n" +"The callbacks are scheduled to be called as soon as possible. Also\n" +"clears the callback list."); + +#define _ASYNCIO_FUTURE__FINISH_EXECUTION_METHODDEF \ + {"_finish_execution", (PyCFunction)_asyncio_Future__finish_execution, METH_NOARGS, _asyncio_Future__finish_execution__doc__}, + +static PyObject * +_asyncio_Future__finish_execution_impl(FutureObj *self); + +static PyObject * +_asyncio_Future__finish_execution(FutureObj *self, PyObject *Py_UNUSED(ignored)) +{ + return _asyncio_Future__finish_execution_impl(self); +} + PyDoc_STRVAR(_asyncio_Task___init____doc__, "Task(coro, *, loop=None, name=None, context=None, eager_start=False)\n" "--\n" @@ -940,6 +961,23 @@ PyDoc_STRVAR(_asyncio_Task_set_name__doc__, #define _ASYNCIO_TASK_SET_NAME_METHODDEF \ {"set_name", (PyCFunction)_asyncio_Task_set_name, METH_O, _asyncio_Task_set_name__doc__}, +PyDoc_STRVAR(_asyncio_Task__finish_execution__doc__, +"_finish_execution($self, /)\n" +"--\n" +"\n"); + +#define _ASYNCIO_TASK__FINISH_EXECUTION_METHODDEF \ + {"_finish_execution", (PyCFunction)_asyncio_Task__finish_execution, METH_NOARGS, _asyncio_Task__finish_execution__doc__}, + +static PyObject * +_asyncio_Task__finish_execution_impl(TaskObj *self); + +static PyObject * +_asyncio_Task__finish_execution(TaskObj *self, PyObject *Py_UNUSED(ignored)) +{ + return _asyncio_Task__finish_execution_impl(self); +} + PyDoc_STRVAR(_asyncio__get_running_loop__doc__, "_get_running_loop($module, /)\n" "--\n" @@ -1547,4 +1585,4 @@ _asyncio_all_tasks(PyObject *module, PyObject *const *args, Py_ssize_t nargs, Py exit: return return_value; } -/*[clinic end generated code: output=ffe9b71bc65888b3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=295cbb5db45fd5a5 input=a9049054013a1b77]*/