@@ -1143,18 +1143,24 @@ struct _mem_work_chunk {
11431143 struct _mem_work_item array [WORK_ITEMS_PER_CHUNK ];
11441144};
11451145
1146+ static int
1147+ work_item_should_decref (uintptr_t ptr )
1148+ {
1149+ return ptr & 0x01 ;
1150+ }
1151+
11461152static void
11471153free_work_item (uintptr_t ptr , delayed_dealloc_cb cb , void * state )
11481154{
1149- if (ptr & 0x01 ) {
1155+ if (work_item_should_decref ( ptr ) ) {
11501156 PyObject * obj = (PyObject * )(ptr - 1 );
11511157#ifdef Py_GIL_DISABLED
11521158 if (cb == NULL ) {
11531159 assert (!_PyInterpreterState_GET ()-> stoptheworld .world_stopped );
11541160 Py_DECREF (obj );
11551161 return ;
11561162 }
1157-
1163+ assert ( _PyInterpreterState_GET () -> stoptheworld . world_stopped );
11581164 Py_ssize_t refcount = _Py_ExplicitMergeRefcount (obj , -1 );
11591165 if (refcount == 0 ) {
11601166 cb (obj , state );
@@ -1180,7 +1186,7 @@ free_delayed(uintptr_t ptr)
11801186 {
11811187 // Free immediately during interpreter shutdown or if the world is
11821188 // stopped.
1183- assert (!interp -> stoptheworld .world_stopped || !(ptr & 0x01 ));
1189+ assert (!interp -> stoptheworld .world_stopped || !work_item_should_decref (ptr ));
11841190 free_work_item (ptr , NULL , NULL );
11851191 return ;
11861192 }
@@ -1207,10 +1213,22 @@ free_delayed(uintptr_t ptr)
12071213
12081214 if (buf == NULL ) {
12091215 // failed to allocate a buffer, free immediately
1216+ PyObject * to_dealloc = NULL ;
12101217 _PyEval_StopTheWorld (tstate -> base .interp );
1211- // TODO: Fix me
1212- free_work_item (ptr , NULL , NULL );
1218+ if (work_item_should_decref (ptr )) {
1219+ PyObject * obj = (PyObject * )(ptr - 1 );
1220+ Py_ssize_t refcount = _Py_ExplicitMergeRefcount (obj , -1 );
1221+ if (refcount == 0 ) {
1222+ to_dealloc = obj ;
1223+ }
1224+ }
1225+ else {
1226+ PyMem_Free ((void * )ptr );
1227+ }
12131228 _PyEval_StartTheWorld (tstate -> base .interp );
1229+ if (to_dealloc != NULL ) {
1230+ _Py_Dealloc (to_dealloc );
1231+ }
12141232 return ;
12151233 }
12161234
@@ -1257,14 +1275,16 @@ process_queue(struct llist_node *head, struct _qsbr_thread_state *qsbr,
12571275 while (!llist_empty (head )) {
12581276 struct _mem_work_chunk * buf = work_queue_first (head );
12591277
1260- while (buf -> rd_idx < buf -> wr_idx ) {
1278+ if (buf -> rd_idx < buf -> wr_idx ) {
12611279 struct _mem_work_item * item = & buf -> array [buf -> rd_idx ];
12621280 if (!_Py_qsbr_poll (qsbr , item -> qsbr_goal )) {
12631281 return ;
12641282 }
12651283
1266- free_work_item (item -> ptr , cb , state );
12671284 buf -> rd_idx ++ ;
1285+ // NB: free_work_item may re-enter or execute arbitrary code
1286+ free_work_item (item -> ptr , cb , state );
1287+ continue ;
12681288 }
12691289
12701290 assert (buf -> rd_idx == buf -> wr_idx );
@@ -1360,12 +1380,14 @@ _PyMem_FiniDelayed(PyInterpreterState *interp)
13601380 while (!llist_empty (head )) {
13611381 struct _mem_work_chunk * buf = work_queue_first (head );
13621382
1363- while (buf -> rd_idx < buf -> wr_idx ) {
1383+ if (buf -> rd_idx < buf -> wr_idx ) {
13641384 // Free the remaining items immediately. There should be no other
13651385 // threads accessing the memory at this point during shutdown.
13661386 struct _mem_work_item * item = & buf -> array [buf -> rd_idx ];
1367- free_work_item (item -> ptr , NULL , NULL );
13681387 buf -> rd_idx ++ ;
1388+ // NB: free_work_item may re-enter or execute arbitrary code
1389+ free_work_item (item -> ptr , NULL , NULL );
1390+ continue ;
13691391 }
13701392
13711393 llist_remove (& buf -> node );
0 commit comments