@@ -19,6 +19,8 @@ typedef struct _table_entry {
1919 int linenumber ;
2020 const char * filename_borrow ;
2121 int linenumber_borrow ;
22+ int borrows ;
23+ _PyStackRef borrowed_from ;
2224} TableEntry ;
2325
2426TableEntry *
@@ -34,6 +36,8 @@ make_table_entry(PyObject *obj, const char *filename, int linenumber)
3436 result -> linenumber = linenumber ;
3537 result -> filename_borrow = NULL ;
3638 result -> linenumber_borrow = 0 ;
39+ result -> borrows = 0 ;
40+ result -> borrowed_from = PyStackRef_NULL ;
3741 return result ;
3842}
3943
@@ -47,11 +51,13 @@ _Py_stackref_get_object(_PyStackRef ref)
4751 PyInterpreterState * interp = PyInterpreterState_Get ();
4852 assert (interp != NULL );
4953 if (ref .index >= interp -> next_stackref ) {
50- _Py_FatalErrorFormat (__func__ , "Garbled stack ref with ID %" PRIu64 "\n" , ref .index );
54+ _Py_FatalErrorFormat (__func__ ,
55+ "Garbled stack ref with ID %" PRIu64 "\n" , ref .index );
5156 }
5257 TableEntry * entry = _Py_hashtable_get (interp -> open_stackrefs_table , (void * )ref .index );
5358 if (entry == NULL ) {
54- _Py_FatalErrorFormat (__func__ , "Accessing closed stack ref with ID %" PRIu64 "\n" , ref .index );
59+ _Py_FatalErrorFormat (__func__ ,
60+ "Accessing closed stack ref with ID %" PRIu64 "\n" , ref .index );
5561 }
5662 return entry -> obj ;
5763}
@@ -68,13 +74,16 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber)
6874 assert (!PyStackRef_IsError (ref ));
6975 PyInterpreterState * interp = PyInterpreterState_Get ();
7076 if (ref .index >= interp -> next_stackref ) {
71- _Py_FatalErrorFormat (__func__ , "Invalid StackRef with ID %" PRIu64 " at %s:%d\n" , (void * )ref .index , filename , linenumber );
72-
77+ _Py_FatalErrorFormat (__func__ ,
78+ "Invalid StackRef with ID %" PRIu64 " at %s:%d\n" ,
79+ ref .index , filename , linenumber );
7380 }
7481 PyObject * obj ;
7582 if (ref .index < INITIAL_STACKREF_INDEX ) {
7683 if (ref .index == 0 ) {
77- _Py_FatalErrorFormat (__func__ , "Passing NULL to PyStackRef_CLOSE at %s:%d\n" , filename , linenumber );
84+ _Py_FatalErrorFormat (__func__ ,
85+ "Passing NULL to _Py_stackref_close at %s:%d\n" ,
86+ filename , linenumber );
7887 }
7988 // Pre-allocated reference to None, False or True -- Do not clear
8089 TableEntry * entry = _Py_hashtable_get (interp -> open_stackrefs_table , (void * )ref .index );
@@ -88,10 +97,27 @@ _Py_stackref_close(_PyStackRef ref, const char *filename, int linenumber)
8897 if (entry != NULL ) {
8998 _Py_FatalErrorFormat (__func__ ,
9099 "Double close of ref ID %" PRIu64 " at %s:%d. Referred to instance of %s at %p. Closed at %s:%d\n" ,
91- ( void * ) ref .index , filename , linenumber , entry -> classname , entry -> obj , entry -> filename , entry -> linenumber );
100+ ref .index , filename , linenumber , entry -> classname , entry -> obj , entry -> filename , entry -> linenumber );
92101 }
93102#endif
94- _Py_FatalErrorFormat (__func__ , "Invalid StackRef with ID %" PRIu64 "\n" , (void * )ref .index );
103+ _Py_FatalErrorFormat (__func__ ,
104+ "Invalid StackRef with ID %" PRIu64 " at %s:%d\n" ,
105+ ref .index , filename , linenumber );
106+ }
107+ if (!PyStackRef_IsNull (entry -> borrowed_from )) {
108+ _PyStackRef borrowed_from = entry -> borrowed_from ;
109+ TableEntry * entry_borrowed = _Py_hashtable_get (interp -> open_stackrefs_table , (void * )borrowed_from .index );
110+ if (entry_borrowed == NULL ) {
111+ _Py_FatalErrorFormat (__func__ ,
112+ "Invalid borrowed StackRef with ID %" PRIu64 " at %s:%d\n" ,
113+ borrowed_from .index , filename , linenumber );
114+ }
115+ entry_borrowed -> borrows -- ;
116+ }
117+ if (entry -> borrows > 0 ) {
118+ _Py_FatalErrorFormat (__func__ ,
119+ "StackRef with ID %" PRIu64 " closed with %d borrowed refs at %s:%d. Opened at %s:%d\n" ,
120+ ref .index , entry -> borrows , filename , linenumber , entry -> filename , entry -> linenumber );
95121 }
96122 obj = entry -> obj ;
97123 free (entry );
@@ -143,15 +169,62 @@ _Py_stackref_record_borrow(_PyStackRef ref, const char *filename, int linenumber
143169 if (entry != NULL ) {
144170 _Py_FatalErrorFormat (__func__ ,
145171 "Borrow of closed ref ID %" PRIu64 " at %s:%d. Referred to instance of %s at %p. Closed at %s:%d\n" ,
146- ( void * ) ref .index , filename , linenumber , entry -> classname , entry -> obj , entry -> filename , entry -> linenumber );
172+ ref .index , filename , linenumber , entry -> classname , entry -> obj , entry -> filename , entry -> linenumber );
147173 }
148174#endif
149- _Py_FatalErrorFormat (__func__ , "Invalid StackRef with ID %" PRIu64 " at %s:%d\n" , (void * )ref .index , filename , linenumber );
175+ _Py_FatalErrorFormat (__func__ ,
176+ "Invalid StackRef with ID %" PRIu64 " at %s:%d\n" ,
177+ ref .index , filename , linenumber );
150178 }
151179 entry -> filename_borrow = filename ;
152180 entry -> linenumber_borrow = linenumber ;
153181}
154182
183+ _PyStackRef
184+ _Py_stackref_get_borrowed_from (_PyStackRef ref , const char * filename , int linenumber )
185+ {
186+ assert (!PyStackRef_IsError (ref ));
187+ PyInterpreterState * interp = PyInterpreterState_Get ();
188+
189+ TableEntry * entry = _Py_hashtable_get (interp -> open_stackrefs_table , (void * )ref .index );
190+ if (entry == NULL ) {
191+ _Py_FatalErrorFormat (__func__ ,
192+ "Invalid StackRef with ID %" PRIu64 " at %s:%d\n" ,
193+ ref .index , filename , linenumber );
194+ }
195+
196+ return entry -> borrowed_from ;
197+ }
198+
199+ // This function should be used no more than once per ref.
200+ void
201+ _Py_stackref_set_borrowed_from (_PyStackRef ref , _PyStackRef borrowed_from , const char * filename , int linenumber )
202+ {
203+ assert (!PyStackRef_IsError (ref ));
204+ PyInterpreterState * interp = PyInterpreterState_Get ();
205+
206+ TableEntry * entry = _Py_hashtable_get (interp -> open_stackrefs_table , (void * )ref .index );
207+ if (entry == NULL ) {
208+ _Py_FatalErrorFormat (__func__ ,
209+ "Invalid StackRef (ref) with ID %" PRIu64 " at %s:%d\n" ,
210+ ref .index , filename , linenumber );
211+ }
212+
213+ assert (PyStackRef_IsNull (entry -> borrowed_from ));
214+ if (PyStackRef_IsNull (borrowed_from )) {
215+ return ;
216+ }
217+
218+ TableEntry * entry_borrowed = _Py_hashtable_get (interp -> open_stackrefs_table , (void * )borrowed_from .index );
219+ if (entry_borrowed == NULL ) {
220+ _Py_FatalErrorFormat (__func__ ,
221+ "Invalid StackRef (borrowed_from) with ID %" PRIu64 " at %s:%d\n" ,
222+ borrowed_from .index , filename , linenumber );
223+ }
224+
225+ entry -> borrowed_from = borrowed_from ;
226+ entry_borrowed -> borrows ++ ;
227+ }
155228
156229void
157230_Py_stackref_associate (PyInterpreterState * interp , PyObject * obj , _PyStackRef ref )
0 commit comments