@@ -74,6 +74,20 @@ module gc
74
74
#define AS_GC (op ) _Py_AS_GC(op)
75
75
#define FROM_GC (gc ) _Py_FROM_GC(gc)
76
76
77
+ // Automatically choose the generation that needs collecting.
78
+ #define GENERATION_AUTO (-1)
79
+
80
+ typedef enum {
81
+ // GC was triggered by heap allocation
82
+ _Py_GC_REASON_HEAP ,
83
+
84
+ // GC was called during shutdown
85
+ _Py_GC_REASON_SHUTDOWN ,
86
+
87
+ // GC was called by gc.collect() or PyGC_Collect()
88
+ _Py_GC_REASON_MANUAL
89
+ } _PyGC_Reason ;
90
+
77
91
78
92
static inline int
79
93
gc_is_collecting (PyGC_Head * g )
@@ -1192,14 +1206,20 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
1192
1206
gc_list_merge (resurrected , old_generation );
1193
1207
}
1194
1208
1209
+
1210
+ static void
1211
+ invoke_gc_callback (PyThreadState * tstate , const char * phase ,
1212
+ int generation , Py_ssize_t collected ,
1213
+ Py_ssize_t uncollectable );
1214
+
1215
+ static int
1216
+ gc_select_generation (GCState * gcstate );
1217
+
1195
1218
/* This is the main function. Read this to understand how the
1196
1219
* collection process works. */
1197
1220
static Py_ssize_t
1198
- gc_collect_main (PyThreadState * tstate , int generation ,
1199
- Py_ssize_t * n_collected , Py_ssize_t * n_uncollectable ,
1200
- int nofail )
1221
+ gc_collect_main (PyThreadState * tstate , int generation , _PyGC_Reason reason )
1201
1222
{
1202
- GC_STAT_ADD (generation , collections , 1 );
1203
1223
#ifdef Py_STATS
1204
1224
if (_Py_stats ) {
1205
1225
_Py_stats -> object_stats .object_visits = 0 ;
@@ -1221,6 +1241,31 @@ gc_collect_main(PyThreadState *tstate, int generation,
1221
1241
assert (gcstate -> garbage != NULL );
1222
1242
assert (!_PyErr_Occurred (tstate ));
1223
1243
1244
+ int expected = 0 ;
1245
+ if (!_Py_atomic_compare_exchange_int (& gcstate -> collecting , & expected , 1 )) {
1246
+ // Don't start a garbage collection if one is already in progress.
1247
+ return 0 ;
1248
+ }
1249
+
1250
+ if (generation == GENERATION_AUTO ) {
1251
+ // Select the oldest generation that needs collecting. We will collect
1252
+ // objects from that generation and all generations younger than it.
1253
+ generation = gc_select_generation (gcstate );
1254
+ if (generation < 0 ) {
1255
+ // No generation needs to be collected.
1256
+ _Py_atomic_store_int (& gcstate -> collecting , 0 );
1257
+ return 0 ;
1258
+ }
1259
+ }
1260
+
1261
+ assert (generation >= 0 && generation < NUM_GENERATIONS );
1262
+
1263
+ GC_STAT_ADD (generation , collections , 1 );
1264
+
1265
+ if (reason != _Py_GC_REASON_SHUTDOWN ) {
1266
+ invoke_gc_callback (tstate , "start" , generation , 0 , 0 );
1267
+ }
1268
+
1224
1269
if (gcstate -> debug & DEBUG_STATS ) {
1225
1270
PySys_WriteStderr ("gc: collecting generation %d...\n" , generation );
1226
1271
show_stats_each_generations (gcstate );
@@ -1340,7 +1385,7 @@ gc_collect_main(PyThreadState *tstate, int generation,
1340
1385
}
1341
1386
1342
1387
if (_PyErr_Occurred (tstate )) {
1343
- if (nofail ) {
1388
+ if (reason == _Py_GC_REASON_SHUTDOWN ) {
1344
1389
_PyErr_Clear (tstate );
1345
1390
}
1346
1391
else {
@@ -1349,13 +1394,6 @@ gc_collect_main(PyThreadState *tstate, int generation,
1349
1394
}
1350
1395
1351
1396
/* Update stats */
1352
- if (n_collected ) {
1353
- * n_collected = m ;
1354
- }
1355
- if (n_uncollectable ) {
1356
- * n_uncollectable = n ;
1357
- }
1358
-
1359
1397
struct gc_generation_stats * stats = & gcstate -> generation_stats [generation ];
1360
1398
stats -> collections ++ ;
1361
1399
stats -> collected += m ;
@@ -1374,7 +1412,12 @@ gc_collect_main(PyThreadState *tstate, int generation,
1374
1412
PyDTrace_GC_DONE (n + m );
1375
1413
}
1376
1414
1415
+ if (reason != _Py_GC_REASON_SHUTDOWN ) {
1416
+ invoke_gc_callback (tstate , "stop" , generation , m , n );
1417
+ }
1418
+
1377
1419
assert (!_PyErr_Occurred (tstate ));
1420
+ _Py_atomic_store_int (& gcstate -> collecting , 0 );
1378
1421
return n + m ;
1379
1422
}
1380
1423
@@ -1433,29 +1476,12 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase,
1433
1476
assert (!_PyErr_Occurred (tstate ));
1434
1477
}
1435
1478
1436
- /* Perform garbage collection of a generation and invoke
1437
- * progress callbacks.
1438
- */
1439
- static Py_ssize_t
1440
- gc_collect_with_callback (PyThreadState * tstate , int generation )
1441
- {
1442
- assert (!_PyErr_Occurred (tstate ));
1443
- Py_ssize_t result , collected , uncollectable ;
1444
- invoke_gc_callback (tstate , "start" , generation , 0 , 0 );
1445
- result = gc_collect_main (tstate , generation , & collected , & uncollectable , 0 );
1446
- invoke_gc_callback (tstate , "stop" , generation , collected , uncollectable );
1447
- assert (!_PyErr_Occurred (tstate ));
1448
- return result ;
1449
- }
1450
-
1451
- static Py_ssize_t
1452
- gc_collect_generations (PyThreadState * tstate )
1479
+ /* Find the oldest generation (highest numbered) where the count
1480
+ * exceeds the threshold. Objects in the that generation and
1481
+ * generations younger than it will be collected. */
1482
+ static int
1483
+ gc_select_generation (GCState * gcstate )
1453
1484
{
1454
- GCState * gcstate = & tstate -> interp -> gc ;
1455
- /* Find the oldest generation (highest numbered) where the count
1456
- * exceeds the threshold. Objects in the that generation and
1457
- * generations younger than it will be collected. */
1458
- Py_ssize_t n = 0 ;
1459
1485
for (int i = NUM_GENERATIONS - 1 ; i >= 0 ; i -- ) {
1460
1486
if (gcstate -> generations [i ].count > gcstate -> generations [i ].threshold ) {
1461
1487
/* Avoid quadratic performance degradation in number
@@ -1497,13 +1523,16 @@ gc_collect_generations(PyThreadState *tstate)
1497
1523
if (i == NUM_GENERATIONS - 1
1498
1524
&& gcstate -> long_lived_pending < gcstate -> long_lived_total / 4 )
1499
1525
continue ;
1500
- n = gc_collect_with_callback (tstate , i );
1501
- break ;
1526
+ return i ;
1502
1527
}
1503
1528
}
1504
- return n ;
1529
+ return -1 ;
1505
1530
}
1506
1531
1532
+
1533
+
1534
+
1535
+
1507
1536
#include "clinic/gcmodule.c.h"
1508
1537
1509
1538
/*[clinic input]
@@ -1572,18 +1601,7 @@ gc_collect_impl(PyObject *module, int generation)
1572
1601
return -1 ;
1573
1602
}
1574
1603
1575
- GCState * gcstate = & tstate -> interp -> gc ;
1576
- Py_ssize_t n ;
1577
- if (gcstate -> collecting ) {
1578
- /* already collecting, don't do anything */
1579
- n = 0 ;
1580
- }
1581
- else {
1582
- gcstate -> collecting = 1 ;
1583
- n = gc_collect_with_callback (tstate , generation );
1584
- gcstate -> collecting = 0 ;
1585
- }
1586
- return n ;
1604
+ return gc_collect_main (tstate , generation , _Py_GC_REASON_MANUAL );
1587
1605
}
1588
1606
1589
1607
/*[clinic input]
@@ -2120,17 +2138,9 @@ PyGC_Collect(void)
2120
2138
}
2121
2139
2122
2140
Py_ssize_t n ;
2123
- if (gcstate -> collecting ) {
2124
- /* already collecting, don't do anything */
2125
- n = 0 ;
2126
- }
2127
- else {
2128
- gcstate -> collecting = 1 ;
2129
- PyObject * exc = _PyErr_GetRaisedException (tstate );
2130
- n = gc_collect_with_callback (tstate , NUM_GENERATIONS - 1 );
2131
- _PyErr_SetRaisedException (tstate , exc );
2132
- gcstate -> collecting = 0 ;
2133
- }
2141
+ PyObject * exc = _PyErr_GetRaisedException (tstate );
2142
+ n = gc_collect_main (tstate , NUM_GENERATIONS - 1 , _Py_GC_REASON_MANUAL );
2143
+ _PyErr_SetRaisedException (tstate , exc );
2134
2144
2135
2145
return n ;
2136
2146
}
@@ -2144,16 +2154,7 @@ _PyGC_CollectNoFail(PyThreadState *tstate)
2144
2154
during interpreter shutdown (and then never finish it).
2145
2155
See http://bugs.python.org/issue8713#msg195178 for an example.
2146
2156
*/
2147
- GCState * gcstate = & tstate -> interp -> gc ;
2148
- if (gcstate -> collecting ) {
2149
- return 0 ;
2150
- }
2151
-
2152
- Py_ssize_t n ;
2153
- gcstate -> collecting = 1 ;
2154
- n = gc_collect_main (tstate , NUM_GENERATIONS - 1 , NULL , NULL , 1 );
2155
- gcstate -> collecting = 0 ;
2156
- return n ;
2157
+ return gc_collect_main (tstate , NUM_GENERATIONS - 1 , _Py_GC_REASON_SHUTDOWN );
2157
2158
}
2158
2159
2159
2160
void
@@ -2271,10 +2272,6 @@ PyObject_IS_GC(PyObject *obj)
2271
2272
void
2272
2273
_Py_ScheduleGC (PyInterpreterState * interp )
2273
2274
{
2274
- GCState * gcstate = & interp -> gc ;
2275
- if (gcstate -> collecting == 1 ) {
2276
- return ;
2277
- }
2278
2275
_Py_set_eval_breaker_bit (interp , _PY_GC_SCHEDULED_BIT , 1 );
2279
2276
}
2280
2277
@@ -2292,7 +2289,7 @@ _PyObject_GC_Link(PyObject *op)
2292
2289
if (gcstate -> generations [0 ].count > gcstate -> generations [0 ].threshold &&
2293
2290
gcstate -> enabled &&
2294
2291
gcstate -> generations [0 ].threshold &&
2295
- !gcstate -> collecting &&
2292
+ !_Py_atomic_load_int_relaxed ( & gcstate -> collecting ) &&
2296
2293
!_PyErr_Occurred (tstate ))
2297
2294
{
2298
2295
_Py_ScheduleGC (tstate -> interp );
@@ -2302,10 +2299,7 @@ _PyObject_GC_Link(PyObject *op)
2302
2299
void
2303
2300
_Py_RunGC (PyThreadState * tstate )
2304
2301
{
2305
- GCState * gcstate = & tstate -> interp -> gc ;
2306
- gcstate -> collecting = 1 ;
2307
- gc_collect_generations (tstate );
2308
- gcstate -> collecting = 0 ;
2302
+ gc_collect_main (tstate , GENERATION_AUTO , _Py_GC_REASON_HEAP );
2309
2303
}
2310
2304
2311
2305
static PyObject *
0 commit comments