@@ -2225,22 +2225,20 @@ Py_ReprLeave(PyObject *obj)
2225
2225
* object, with refcount 0. Py_DECREF must already have been called on it.
2226
2226
*/
2227
2227
static void
2228
- _PyTrash_thread_deposit_object (PyObject * op )
2228
+ _PyTrash_thread_deposit_object (struct _py_trashcan * trash , PyObject * op )
2229
2229
{
2230
- PyThreadState * tstate = _PyThreadState_GET ();
2231
2230
_PyObject_ASSERT (op , _PyObject_IS_GC (op ));
2232
2231
_PyObject_ASSERT (op , !_PyObject_GC_IS_TRACKED (op ));
2233
2232
_PyObject_ASSERT (op , Py_REFCNT (op ) == 0 );
2234
- _PyGCHead_SET_PREV (_Py_AS_GC (op ), (PyGC_Head * )tstate -> trash_delete_later );
2235
- tstate -> trash_delete_later = op ;
2233
+ _PyGCHead_SET_PREV (_Py_AS_GC (op ), (PyGC_Head * )trash -> delete_later );
2234
+ trash -> delete_later = op ;
2236
2235
}
2237
2236
2238
2237
/* Deallocate all the objects in the gcstate->trash_delete_later list.
2239
2238
* Called when the call-stack unwinds again. */
2240
2239
static void
2241
- _PyTrash_thread_destroy_chain (void )
2240
+ _PyTrash_thread_destroy_chain (struct _py_trashcan * trash )
2242
2241
{
2243
- PyThreadState * tstate = _PyThreadState_GET ();
2244
2242
/* We need to increase trash_delete_nesting here, otherwise,
2245
2243
_PyTrash_thread_destroy_chain will be called recursively
2246
2244
and then possibly crash. An example that may crash without
@@ -2252,13 +2250,13 @@ _PyTrash_thread_destroy_chain(void)
2252
2250
tups = [(tup,) for tup in tups]
2253
2251
del tups
2254
2252
*/
2255
- assert (tstate -> trash_delete_nesting == 0 );
2256
- ++ tstate -> trash_delete_nesting ;
2257
- while (tstate -> trash_delete_later ) {
2258
- PyObject * op = tstate -> trash_delete_later ;
2253
+ assert (trash -> delete_nesting == 0 );
2254
+ ++ trash -> delete_nesting ;
2255
+ while (trash -> delete_later ) {
2256
+ PyObject * op = trash -> delete_later ;
2259
2257
destructor dealloc = Py_TYPE (op )-> tp_dealloc ;
2260
2258
2261
- tstate -> trash_delete_later =
2259
+ trash -> delete_later =
2262
2260
(PyObject * ) _PyGCHead_PREV (_Py_AS_GC (op ));
2263
2261
2264
2262
/* Call the deallocator directly. This used to try to
@@ -2269,32 +2267,79 @@ _PyTrash_thread_destroy_chain(void)
2269
2267
*/
2270
2268
_PyObject_ASSERT (op , Py_REFCNT (op ) == 0 );
2271
2269
(* dealloc )(op );
2272
- assert (tstate -> trash_delete_nesting == 1 );
2270
+ assert (trash -> delete_nesting == 1 );
2271
+ }
2272
+ -- trash -> delete_nesting ;
2273
+ }
2274
+
2275
+
2276
+ static struct _py_trashcan *
2277
+ _PyTrash_get_state (PyThreadState * tstate )
2278
+ {
2279
+ if (tstate != NULL ) {
2280
+ return & tstate -> trash ;
2281
+ }
2282
+ // The current thread must be finalizing.
2283
+ // Fall back to using thread-local state.
2284
+ // XXX Use thread-local variable syntax?
2285
+ assert (PyThread_tss_is_created (& _PyRuntime .trashTSSkey ));
2286
+ struct _py_trashcan * trash =
2287
+ (struct _py_trashcan * )PyThread_tss_get (& _PyRuntime .trashTSSkey );
2288
+ if (trash == NULL ) {
2289
+ trash = PyMem_RawMalloc (sizeof (struct _py_trashcan ));
2290
+ if (trash == NULL ) {
2291
+ Py_FatalError ("Out of memory" );
2292
+ }
2293
+ PyThread_tss_set (& _PyRuntime .trashTSSkey , (void * )trash );
2294
+ }
2295
+ return trash ;
2296
+ }
2297
+
2298
+ static void
2299
+ _PyTrash_clear_state (PyThreadState * tstate )
2300
+ {
2301
+ if (tstate != NULL ) {
2302
+ assert (tstate -> trash .delete_later == NULL );
2303
+ return ;
2304
+ }
2305
+ if (PyThread_tss_is_created (& _PyRuntime .trashTSSkey )) {
2306
+ struct _py_trashcan * trash =
2307
+ (struct _py_trashcan * )PyThread_tss_get (& _PyRuntime .trashTSSkey );
2308
+ if (trash != NULL ) {
2309
+ PyThread_tss_set (& _PyRuntime .trashTSSkey , (void * )NULL );
2310
+ PyMem_RawFree (trash );
2311
+ }
2273
2312
}
2274
- -- tstate -> trash_delete_nesting ;
2275
2313
}
2276
2314
2277
2315
2278
2316
int
2279
2317
_PyTrash_begin (PyThreadState * tstate , PyObject * op )
2280
2318
{
2281
- if (tstate -> trash_delete_nesting >= _PyTrash_UNWIND_LEVEL ) {
2319
+ // XXX Make sure the GIL is held.
2320
+ struct _py_trashcan * trash = _PyTrash_get_state (tstate );
2321
+ if (trash -> delete_nesting >= _PyTrash_UNWIND_LEVEL ) {
2282
2322
/* Store the object (to be deallocated later) and jump past
2283
2323
* Py_TRASHCAN_END, skipping the body of the deallocator */
2284
- _PyTrash_thread_deposit_object (op );
2324
+ _PyTrash_thread_deposit_object (trash , op );
2285
2325
return 1 ;
2286
2326
}
2287
- ++ tstate -> trash_delete_nesting ;
2327
+ ++ trash -> delete_nesting ;
2288
2328
return 0 ;
2289
2329
}
2290
2330
2291
2331
2292
2332
void
2293
2333
_PyTrash_end (PyThreadState * tstate )
2294
2334
{
2295
- -- tstate -> trash_delete_nesting ;
2296
- if (tstate -> trash_delete_later && tstate -> trash_delete_nesting <= 0 ) {
2297
- _PyTrash_thread_destroy_chain ();
2335
+ // XXX Make sure the GIL is held.
2336
+ struct _py_trashcan * trash = _PyTrash_get_state (tstate );
2337
+ -- trash -> delete_nesting ;
2338
+ if (trash -> delete_nesting <= 0 ) {
2339
+ if (trash -> delete_later != NULL ) {
2340
+ _PyTrash_thread_destroy_chain (trash );
2341
+ }
2342
+ _PyTrash_clear_state (tstate );
2298
2343
}
2299
2344
}
2300
2345
@@ -2371,7 +2416,7 @@ _Py_Dealloc(PyObject *op)
2371
2416
destructor dealloc = type -> tp_dealloc ;
2372
2417
#ifdef Py_DEBUG
2373
2418
PyThreadState * tstate = _PyThreadState_GET ();
2374
- PyObject * old_exc_type = tstate -> curexc_type ;
2419
+ PyObject * old_exc_type = tstate != NULL ? tstate -> curexc_type : NULL ;
2375
2420
// Keep the old exception type alive to prevent undefined behavior
2376
2421
// on (tstate->curexc_type != old_exc_type) below
2377
2422
Py_XINCREF (old_exc_type );
@@ -2387,7 +2432,7 @@ _Py_Dealloc(PyObject *op)
2387
2432
#ifdef Py_DEBUG
2388
2433
// gh-89373: The tp_dealloc function must leave the current exception
2389
2434
// unchanged.
2390
- if (tstate -> curexc_type != old_exc_type ) {
2435
+ if (tstate != NULL && tstate -> curexc_type != old_exc_type ) {
2391
2436
const char * err ;
2392
2437
if (old_exc_type == NULL ) {
2393
2438
err = "Deallocator of type '%s' raised an exception" ;
0 commit comments