Skip to content

Commit 4068cf8

Browse files
authored
Merge branch 'main' into jit/gc
2 parents 29b4663 + 42d2bed commit 4068cf8

File tree

7 files changed

+70
-31
lines changed

7 files changed

+70
-31
lines changed

Include/internal/pycore_typeobject.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ extern int _PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, PyObject
155155
// Precalculates count of non-unique slots and fills wrapperbase.name_count.
156156
extern int _PyType_InitSlotDefs(PyInterpreterState *interp);
157157

158+
// Like PyType_GetBaseByToken, but does not modify refcounts.
159+
// Cannot fail; arguments must be valid.
160+
PyAPI_FUNC(int)
161+
_PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject **result);
162+
158163
#ifdef __cplusplus
159164
}
160165
#endif

Lib/test/test_asyncio/test_tasks.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3045,6 +3045,26 @@ class BaseTaskIntrospectionTests:
30453045
_enter_task = None
30463046
_leave_task = None
30473047
all_tasks = None
3048+
Task = None
3049+
3050+
def test_register_task_resurrection(self):
3051+
register_task = self._register_task
3052+
class EvilLoop:
3053+
def get_debug(self):
3054+
return False
3055+
3056+
def call_exception_handler(self, context):
3057+
register_task(context["task"])
3058+
3059+
async def coro_fn ():
3060+
pass
3061+
3062+
coro = coro_fn()
3063+
self.addCleanup(coro.close)
3064+
loop = EvilLoop()
3065+
with self.assertRaises(AttributeError):
3066+
self.Task(coro, loop=loop)
3067+
30483068

30493069
def test__register_task_1(self):
30503070
class TaskLike:
@@ -3175,6 +3195,7 @@ class PyIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):
31753195
_leave_task = staticmethod(tasks._py_leave_task)
31763196
all_tasks = staticmethod(tasks._py_all_tasks)
31773197
current_task = staticmethod(tasks._py_current_task)
3198+
Task = tasks._PyTask
31783199

31793200

31803201
@unittest.skipUnless(hasattr(tasks, '_c_register_task'),
@@ -3187,10 +3208,12 @@ class CIntrospectionTests(test_utils.TestCase, BaseTaskIntrospectionTests):
31873208
_leave_task = staticmethod(tasks._c_leave_task)
31883209
all_tasks = staticmethod(tasks._c_all_tasks)
31893210
current_task = staticmethod(tasks._c_current_task)
3211+
Task = tasks._CTask
31903212
else:
31913213
_register_task = _unregister_task = _enter_task = _leave_task = None
31923214

31933215

3216+
31943217
class BaseCurrentLoopTests:
31953218
current_task = None
31963219

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid reference count operations in garbage collection of :mod:`ctypes`
2+
objects.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash when a task gets re-registered during finalization in :mod:`asyncio`. Patch by Kumar Aditya.

Modules/_asynciomodule.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,16 +2990,12 @@ static PyType_Spec Task_spec = {
29902990
static void
29912991
TaskObj_dealloc(PyObject *self)
29922992
{
2993-
_PyObject_ResurrectStart(self);
2994-
// Unregister the task here so that even if any subclass of Task
2995-
// which doesn't end up calling TaskObj_finalize not crashes.
2996-
unregister_task((TaskObj *)self);
2997-
2998-
PyObject_CallFinalizer(self);
2999-
3000-
if (_PyObject_ResurrectEnd(self)) {
3001-
return;
2993+
if (PyObject_CallFinalizerFromDealloc(self) < 0) {
2994+
return; // resurrected
30022995
}
2996+
// unregister the task after finalization so that
2997+
// if the task gets resurrected, it remains registered
2998+
unregister_task((TaskObj *)self);
30032999

30043000
PyTypeObject *tp = Py_TYPE(self);
30053001
PyObject_GC_UnTrack(self);

Modules/_ctypes/ctypes.h

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -608,25 +608,21 @@ PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result)
608608
return _stginfo_from_type(state, Py_TYPE(obj), result);
609609
}
610610

611-
/* A variant of PyStgInfo_FromType that doesn't need the state,
611+
/* A variant of PyStgInfo_FromType that doesn't need the state
612+
* and doesn't modify any refcounts,
612613
* so it can be called from finalization functions when the module
613614
* state is torn down.
614615
*/
615616
static inline StgInfo *
616617
_PyStgInfo_FromType_NoState(PyObject *type)
617618
{
618619
PyTypeObject *PyCType_Type;
619-
if (PyType_GetBaseByToken(Py_TYPE(type), &pyctype_type_spec, &PyCType_Type) < 0) {
620-
return NULL;
621-
}
622-
if (PyCType_Type == NULL) {
623-
PyErr_Format(PyExc_TypeError, "expected a ctypes type, got '%N'", type);
620+
if (_PyType_GetBaseByToken_Borrow(Py_TYPE(type), &pyctype_type_spec, &PyCType_Type) < 0 ||
621+
PyCType_Type == NULL) {
624622
return NULL;
625623
}
626624

627-
StgInfo *info = PyObject_GetTypeData(type, PyCType_Type);
628-
Py_DECREF(PyCType_Type);
629-
return info;
625+
return PyObject_GetTypeData(type, PyCType_Type);
630626
}
631627

632628
// Initialize StgInfo on a newly created type

Objects/typeobject.c

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5877,23 +5877,15 @@ get_base_by_token_recursive(PyObject *bases, void *token)
58775877
}
58785878

58795879
int
5880-
PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
5880+
_PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject **result)
58815881
{
5882+
assert(token != NULL);
5883+
assert(PyType_Check(type));
5884+
58825885
if (result != NULL) {
58835886
*result = NULL;
58845887
}
58855888

5886-
if (token == NULL) {
5887-
PyErr_Format(PyExc_SystemError,
5888-
"PyType_GetBaseByToken called with token=NULL");
5889-
return -1;
5890-
}
5891-
if (!PyType_Check(type)) {
5892-
PyErr_Format(PyExc_TypeError,
5893-
"expected a type, got a '%T' object", type);
5894-
return -1;
5895-
}
5896-
58975889
if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
58985890
// No static type has a heaptype superclass,
58995891
// which is ensured by type_ready_mro().
@@ -5902,7 +5894,7 @@ PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
59025894
if (((PyHeapTypeObject*)type)->ht_token == token) {
59035895
found:
59045896
if (result != NULL) {
5905-
*result = (PyTypeObject *)Py_NewRef(type);
5897+
*result = type;
59065898
}
59075899
return 1;
59085900
}
@@ -5936,6 +5928,30 @@ PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
59365928
return 0;
59375929
}
59385930

5931+
int
5932+
PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
5933+
{
5934+
if (result != NULL) {
5935+
*result = NULL;
5936+
}
5937+
if (token == NULL) {
5938+
PyErr_Format(PyExc_SystemError,
5939+
"PyType_GetBaseByToken called with token=NULL");
5940+
return -1;
5941+
}
5942+
if (!PyType_Check(type)) {
5943+
PyErr_Format(PyExc_TypeError,
5944+
"expected a type, got a '%T' object", type);
5945+
return -1;
5946+
}
5947+
5948+
int res = _PyType_GetBaseByToken_Borrow(type, token, result);
5949+
if (res > 0 && result) {
5950+
Py_INCREF(*result);
5951+
}
5952+
return res;
5953+
}
5954+
59395955

59405956
void *
59415957
PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls)

0 commit comments

Comments
 (0)