@@ -2888,28 +2888,33 @@ static PyType_Spec pycfuncptr_type_spec = {
28882888 * Code to keep needed objects alive
28892889 */
28902890
2891+ static CDataObject *
2892+ PyCData_GetContainer_lock_held (CDataObject * root )
2893+ {
2894+ assert (root -> b_base == NULL );
2895+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (root );
2896+ if (root -> b_objects == NULL ) {
2897+ if (root -> b_length ) {
2898+ root -> b_objects = PyDict_New ();
2899+ if (root -> b_objects == NULL ) {
2900+ return NULL ;
2901+ }
2902+ } else {
2903+ root -> b_objects = Py_NewRef (Py_None );
2904+ }
2905+ }
2906+ return root ;
2907+ }
2908+
28912909static CDataObject *
28922910PyCData_GetContainer (CDataObject * self )
28932911{
28942912 while (self -> b_base ) {
28952913 self = self -> b_base ;
28962914 }
2897- CDataObject * res = self ;
2915+ CDataObject * res ;
28982916 Py_BEGIN_CRITICAL_SECTION (self );
2899- // avoid using return directly in this block because critical section
2900- // needs to be released before returning
2901- if (self -> b_objects == NULL ) {
2902- if (self -> b_length ) {
2903- self -> b_objects = PyDict_New ();
2904- if (self -> b_objects == NULL ) {
2905- res = NULL ;
2906- goto exit ;
2907- }
2908- } else {
2909- self -> b_objects = Py_NewRef (Py_None );
2910- }
2911- }
2912- exit :;
2917+ res = PyCData_GetContainer_lock_held (self );
29132918 Py_END_CRITICAL_SECTION ();
29142919 return res ;
29152920}
@@ -2924,6 +2929,22 @@ GetKeepedObjects(CDataObject *target)
29242929 return container -> b_objects ;
29252930}
29262931
2932+ /* Like GetKeepedObjects(), for a caller that already holds target's lock. */
2933+ static PyObject *
2934+ GetKeepedObjects_lock_held (CDataObject * target )
2935+ {
2936+ CDataObject * container ;
2937+ if (target -> b_base == NULL ) {
2938+ container = PyCData_GetContainer_lock_held (target );
2939+ }
2940+ else {
2941+ container = PyCData_GetContainer (target );
2942+ }
2943+ if (container == NULL )
2944+ return NULL ;
2945+ return container -> b_objects ;
2946+ }
2947+
29272948static PyObject *
29282949unique_key (CDataObject * target , Py_ssize_t index )
29292950{
@@ -2960,7 +2981,15 @@ KeepRef_lock_held(CDataObject *target, Py_ssize_t index, PyObject *keep)
29602981 Py_DECREF (Py_None );
29612982 return 0 ;
29622983 }
2963- ob = PyCData_GetContainer (target );
2984+ if (target -> b_base == NULL ) {
2985+ // target is its own root and the caller already holds its lock:
2986+ // re-entering the critical section would be a nested same-object
2987+ // lock (failed CAS + slow path) on every field assignment.
2988+ ob = PyCData_GetContainer_lock_held (target );
2989+ }
2990+ else {
2991+ ob = PyCData_GetContainer (target );
2992+ }
29642993 if (ob == NULL ) {
29652994 Py_DECREF (keep );
29662995 return -1 ;
@@ -3564,7 +3593,7 @@ PyCData_set(ctypes_state *st,
35643593 /* KeepRef steals a refcount from it's last argument */
35653594 /* If KeepRef fails, we are stumped. The dst memory block has already
35663595 been changed */
3567- return KeepRef (mem , index , result );
3596+ return KeepRef_lock_held (mem , index , result );
35683597}
35693598
35703599
@@ -5452,8 +5481,9 @@ _ctypes_Simple_value_set_impl(CDataObject *self, PyObject *value)
54525481 if (!result )
54535482 return -1 ;
54545483
5455- /* consumes the refcount the setfunc returns */
5456- return KeepRef (self , 0 , result );
5484+ /* consumes the refcount the setfunc returns; self is already locked
5485+ by the critical section taken by the Argument Clinic wrapper */
5486+ return KeepRef_lock_held (self , 0 , result );
54575487}
54585488
54595489
@@ -5763,15 +5793,15 @@ Pointer_set_contents_lock_held(PyObject *op, PyObject *value, void *closure)
57635793 'value' itself as the second item of the b_objects list, additionally.
57645794 */
57655795 Py_INCREF (value );
5766- if (-1 == KeepRef (self , 1 , value ))
5796+ if (-1 == KeepRef_lock_held (self , 1 , value ))
57675797 return -1 ;
57685798
5769- keep = GetKeepedObjects (dst );
5799+ keep = GetKeepedObjects_lock_held (dst );
57705800 if (keep == NULL )
57715801 return -1 ;
57725802
57735803 Py_INCREF (keep );
5774- return KeepRef (self , 0 , keep );
5804+ return KeepRef_lock_held (self , 0 , keep );
57755805}
57765806
57775807static int
0 commit comments