@@ -75,7 +75,7 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO
75
75
Py_INCREF (executor );
76
76
if (instr -> op .code == ENTER_EXECUTOR ) {
77
77
assert (index == instr -> op .arg );
78
- _Py_ExecutorClear (code -> co_executors -> executors [index ]);
78
+ _Py_ExecutorDetach (code -> co_executors -> executors [index ]);
79
79
}
80
80
else {
81
81
assert (code -> co_executors -> size == index );
@@ -270,10 +270,14 @@ static PyMethodDef executor_methods[] = {
270
270
271
271
///////////////////// Experimental UOp Optimizer /////////////////////
272
272
273
+ static int executor_clear (_PyExecutorObject * executor );
274
+ static void unlink_executor (_PyExecutorObject * executor );
275
+
273
276
static void
274
277
uop_dealloc (_PyExecutorObject * self ) {
275
278
_PyObject_GC_UNTRACK (self );
276
- _Py_ExecutorClear (self );
279
+ assert (self -> vm_data .code == NULL );
280
+ unlink_executor (self );
277
281
#ifdef _Py_JIT
278
282
_PyJIT_Free (self );
279
283
#endif
@@ -379,13 +383,6 @@ PySequenceMethods uop_as_sequence = {
379
383
.sq_item = (ssizeargfunc )uop_item ,
380
384
};
381
385
382
- static int
383
- executor_clear (PyObject * o )
384
- {
385
- _Py_ExecutorClear ((_PyExecutorObject * )o );
386
- return 0 ;
387
- }
388
-
389
386
static int
390
387
executor_traverse (PyObject * o , visitproc visit , void * arg )
391
388
{
@@ -412,7 +409,7 @@ PyTypeObject _PyUOpExecutor_Type = {
412
409
.tp_as_sequence = & uop_as_sequence ,
413
410
.tp_methods = executor_methods ,
414
411
.tp_traverse = executor_traverse ,
415
- .tp_clear = executor_clear ,
412
+ .tp_clear = ( inquiry ) executor_clear ,
416
413
.tp_is_gc = executor_is_gc ,
417
414
};
418
415
@@ -1190,6 +1187,7 @@ init_cold_exit_executor(_PyExecutorObject *executor, int oparg)
1190
1187
inst -> opcode = _COLD_EXIT ;
1191
1188
inst -> oparg = oparg ;
1192
1189
executor -> vm_data .valid = true;
1190
+ executor -> vm_data .linked = false;
1193
1191
for (int i = 0 ; i < BLOOM_FILTER_WORDS ; i ++ ) {
1194
1192
assert (executor -> vm_data .bloom .bits [i ] == 0 );
1195
1193
}
@@ -1328,7 +1326,7 @@ PyTypeObject _PyCounterExecutor_Type = {
1328
1326
.tp_dealloc = (destructor )counter_dealloc ,
1329
1327
.tp_methods = executor_methods ,
1330
1328
.tp_traverse = executor_traverse ,
1331
- .tp_clear = executor_clear ,
1329
+ .tp_clear = ( inquiry ) executor_clear ,
1332
1330
};
1333
1331
1334
1332
static int
@@ -1503,23 +1501,25 @@ link_executor(_PyExecutorObject *executor)
1503
1501
links -> next = NULL ;
1504
1502
}
1505
1503
else {
1506
- _PyExecutorObject * next = head -> vm_data .links .next ;
1507
- links -> previous = head ;
1508
- links -> next = next ;
1509
- if (next != NULL ) {
1510
- next -> vm_data .links .previous = executor ;
1511
- }
1512
- head -> vm_data .links .next = executor ;
1504
+ assert (head -> vm_data .links .previous == NULL );
1505
+ links -> previous = NULL ;
1506
+ links -> next = head ;
1507
+ head -> vm_data .links .previous = executor ;
1508
+ interp -> executor_list_head = executor ;
1513
1509
}
1514
- executor -> vm_data .valid = true;
1510
+ executor -> vm_data .linked = true;
1515
1511
/* executor_list_head must be first in list */
1516
1512
assert (interp -> executor_list_head -> vm_data .links .previous == NULL );
1517
1513
}
1518
1514
1519
1515
static void
1520
1516
unlink_executor (_PyExecutorObject * executor )
1521
1517
{
1518
+ if (!executor -> vm_data .linked ) {
1519
+ return ;
1520
+ }
1522
1521
_PyExecutorLinkListNode * links = & executor -> vm_data .links ;
1522
+ assert (executor -> vm_data .valid );
1523
1523
_PyExecutorObject * next = links -> next ;
1524
1524
_PyExecutorObject * prev = links -> previous ;
1525
1525
if (next != NULL ) {
@@ -1534,7 +1534,7 @@ unlink_executor(_PyExecutorObject *executor)
1534
1534
assert (interp -> executor_list_head == executor );
1535
1535
interp -> executor_list_head = next ;
1536
1536
}
1537
- executor -> vm_data .valid = false;
1537
+ executor -> vm_data .linked = false;
1538
1538
}
1539
1539
1540
1540
/* This must be called by optimizers before using the executor */
@@ -1548,31 +1548,52 @@ _Py_ExecutorInit(_PyExecutorObject *executor, const _PyBloomFilter *dependency_s
1548
1548
link_executor (executor );
1549
1549
}
1550
1550
1551
- /* This must be called by executors during dealloc */
1551
+ /* Detaches the executor from the code object (if any) that
1552
+ * holds a reference to it */
1552
1553
void
1553
- _Py_ExecutorClear (_PyExecutorObject * executor )
1554
+ _Py_ExecutorDetach (_PyExecutorObject * executor )
1554
1555
{
1555
- if (!executor -> vm_data .valid ) {
1556
- return ;
1557
- }
1558
- unlink_executor (executor );
1559
1556
PyCodeObject * code = executor -> vm_data .code ;
1560
1557
if (code == NULL ) {
1561
1558
return ;
1562
1559
}
1563
- for (uint32_t i = 0 ; i < executor -> exit_count ; i ++ ) {
1564
- Py_DECREF (executor -> exits [i ].executor );
1565
- executor -> exits [i ].executor = & COLD_EXITS [i ];
1566
- executor -> exits [i ].temperature = initial_unreachable_backoff_counter ();
1567
- }
1568
1560
_Py_CODEUNIT * instruction = & _PyCode_CODE (code )[executor -> vm_data .index ];
1569
1561
assert (instruction -> op .code == ENTER_EXECUTOR );
1570
1562
int index = instruction -> op .arg ;
1571
1563
assert (code -> co_executors -> executors [index ] == executor );
1572
1564
instruction -> op .code = executor -> vm_data .opcode ;
1573
1565
instruction -> op .arg = executor -> vm_data .oparg ;
1574
1566
executor -> vm_data .code = NULL ;
1575
- Py_CLEAR (code -> co_executors -> executors [index ]);
1567
+ code -> co_executors -> executors [index ] = NULL ;
1568
+ Py_DECREF (executor );
1569
+ }
1570
+
1571
+ static int
1572
+ executor_clear (_PyExecutorObject * executor )
1573
+ {
1574
+ if (!executor -> vm_data .valid ) {
1575
+ return 0 ;
1576
+ }
1577
+ assert (executor -> vm_data .valid == 1 );
1578
+ unlink_executor (executor );
1579
+ executor -> vm_data .valid = 0 ;
1580
+ /* It is possible for an executor to form a reference
1581
+ * cycle with itself, so decref'ing a side exit could
1582
+ * free the executor unless we hold a strong reference to it
1583
+ */
1584
+ Py_INCREF (executor );
1585
+ for (uint32_t i = 0 ; i < executor -> exit_count ; i ++ ) {
1586
+ const _PyExecutorObject * cold = & COLD_EXITS [i ];
1587
+ const _PyExecutorObject * side = executor -> exits [i ].executor ;
1588
+ executor -> exits [i ].temperature = initial_unreachable_backoff_counter ();
1589
+ if (side != cold ) {
1590
+ executor -> exits [i ].executor = cold ;
1591
+ Py_DECREF (side );
1592
+ }
1593
+ }
1594
+ _Py_ExecutorDetach (executor );
1595
+ Py_DECREF (executor );
1596
+ return 0 ;
1576
1597
}
1577
1598
1578
1599
void
@@ -1593,17 +1614,42 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj, int is
1593
1614
_Py_BloomFilter_Add (& obj_filter , obj );
1594
1615
/* Walk the list of executors */
1595
1616
/* TO DO -- Use a tree to avoid traversing as many objects */
1617
+ bool no_memory = false;
1618
+ PyObject * invalidate = PyList_New (0 );
1619
+ if (invalidate == NULL ) {
1620
+ PyErr_Clear ();
1621
+ no_memory = true;
1622
+ }
1623
+ /* Clearing an executor can deallocate others, so we need to make a list of
1624
+ * executors to invalidate first */
1596
1625
for (_PyExecutorObject * exec = interp -> executor_list_head ; exec != NULL ;) {
1597
1626
assert (exec -> vm_data .valid );
1598
1627
_PyExecutorObject * next = exec -> vm_data .links .next ;
1599
1628
if (bloom_filter_may_contain (& exec -> vm_data .bloom , & obj_filter )) {
1600
- _Py_ExecutorClear (exec );
1629
+ unlink_executor (exec );
1630
+ if (no_memory ) {
1631
+ exec -> vm_data .valid = 0 ;
1632
+ } else {
1633
+ if (PyList_Append (invalidate , (PyObject * )exec ) < 0 ) {
1634
+ PyErr_Clear ();
1635
+ no_memory = true;
1636
+ exec -> vm_data .valid = 0 ;
1637
+ }
1638
+ }
1601
1639
if (is_invalidation ) {
1602
1640
OPT_STAT_INC (executors_invalidated );
1603
1641
}
1604
1642
}
1605
1643
exec = next ;
1606
1644
}
1645
+ if (invalidate != NULL ) {
1646
+ for (Py_ssize_t i = 0 ; i < PyList_GET_SIZE (invalidate ); i ++ ) {
1647
+ _PyExecutorObject * exec = (_PyExecutorObject * )PyList_GET_ITEM (invalidate , i );
1648
+ executor_clear (exec );
1649
+ }
1650
+ Py_DECREF (invalidate );
1651
+ }
1652
+ return ;
1607
1653
}
1608
1654
1609
1655
/* Invalidate all executors */
@@ -1612,12 +1658,13 @@ _Py_Executors_InvalidateAll(PyInterpreterState *interp, int is_invalidation)
1612
1658
{
1613
1659
while (interp -> executor_list_head ) {
1614
1660
_PyExecutorObject * executor = interp -> executor_list_head ;
1661
+ assert (executor -> vm_data .valid == 1 && executor -> vm_data .linked == 1 );
1615
1662
if (executor -> vm_data .code ) {
1616
1663
// Clear the entire code object so its co_executors array be freed:
1617
1664
_PyCode_Clear_Executors (executor -> vm_data .code );
1618
1665
}
1619
1666
else {
1620
- _Py_ExecutorClear (executor );
1667
+ executor_clear (executor );
1621
1668
}
1622
1669
if (is_invalidation ) {
1623
1670
OPT_STAT_INC (executors_invalidated );
0 commit comments