mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	GH-114695: Add sys._clear_internal_caches (GH-115152)
				
					
				
			This commit is contained in:
		
							parent
							
								
									54bde5dcc3
								
							
						
					
					
						commit
						235cacff81
					
				
					 12 changed files with 130 additions and 84 deletions
				
			
		| 
						 | 
					@ -195,6 +195,17 @@ always available.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   This function should be used for internal and specialized purposes only.
 | 
					   This function should be used for internal and specialized purposes only.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   .. deprecated:: 3.13
 | 
				
			||||||
 | 
					      Use the more general :func:`_clear_internal_caches` function instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. function:: _clear_internal_caches()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Clear all internal performance-related caches. Use this function *only* to
 | 
				
			||||||
 | 
					   release unnecessary references and memory blocks when hunting for leaks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   .. versionadded:: 3.13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. function:: _current_frames()
 | 
					.. function:: _current_frames()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -724,7 +735,7 @@ always available.
 | 
				
			||||||
   regardless of their size.  This function is mainly useful for tracking
 | 
					   regardless of their size.  This function is mainly useful for tracking
 | 
				
			||||||
   and debugging memory leaks.  Because of the interpreter's internal
 | 
					   and debugging memory leaks.  Because of the interpreter's internal
 | 
				
			||||||
   caches, the result can vary from call to call; you may have to call
 | 
					   caches, the result can vary from call to call; you may have to call
 | 
				
			||||||
   :func:`_clear_type_cache()` and :func:`gc.collect()` to get more
 | 
					   :func:`_clear_internal_caches()` and :func:`gc.collect()` to get more
 | 
				
			||||||
   predictable results.
 | 
					   predictable results.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   If a Python build or implementation cannot reasonably compute this
 | 
					   If a Python build or implementation cannot reasonably compute this
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,9 +24,10 @@ typedef struct {
 | 
				
			||||||
    uint8_t opcode;
 | 
					    uint8_t opcode;
 | 
				
			||||||
    uint8_t oparg;
 | 
					    uint8_t oparg;
 | 
				
			||||||
    uint8_t valid;
 | 
					    uint8_t valid;
 | 
				
			||||||
    uint8_t linked;
 | 
					    int index;           // Index of ENTER_EXECUTOR (if code isn't NULL, below).
 | 
				
			||||||
    _PyBloomFilter bloom;
 | 
					    _PyBloomFilter bloom;
 | 
				
			||||||
    _PyExecutorLinkListNode links;
 | 
					    _PyExecutorLinkListNode links;
 | 
				
			||||||
 | 
					    PyCodeObject *code;  // Weak (NULL if no corresponding ENTER_EXECUTOR).
 | 
				
			||||||
} _PyVMData;
 | 
					} _PyVMData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,8 +201,8 @@ def dash_R_cleanup(fs, ps, pic, zdc, abcs):
 | 
				
			||||||
    # Clear caches
 | 
					    # Clear caches
 | 
				
			||||||
    clear_caches()
 | 
					    clear_caches()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Clear type cache at the end: previous function calls can modify types
 | 
					    # Clear other caches last (previous function calls can re-populate them):
 | 
				
			||||||
    sys._clear_type_cache()
 | 
					    sys._clear_internal_caches()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def warm_caches():
 | 
					def warm_caches():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import contextlib
 | 
					import contextlib
 | 
				
			||||||
import opcode
 | 
					import opcode
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
import textwrap
 | 
					import textwrap
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -181,6 +182,21 @@ def f():
 | 
				
			||||||
        _testinternalcapi.invalidate_executors(f.__code__)
 | 
					        _testinternalcapi.invalidate_executors(f.__code__)
 | 
				
			||||||
        self.assertFalse(exe.is_valid())
 | 
					        self.assertFalse(exe.is_valid())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_sys__clear_internal_caches(self):
 | 
				
			||||||
 | 
					        def f():
 | 
				
			||||||
 | 
					            for _ in range(1000):
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					        opt = _testinternalcapi.get_uop_optimizer()
 | 
				
			||||||
 | 
					        with temporary_optimizer(opt):
 | 
				
			||||||
 | 
					            f()
 | 
				
			||||||
 | 
					        exe = get_first_executor(f)
 | 
				
			||||||
 | 
					        self.assertIsNotNone(exe)
 | 
				
			||||||
 | 
					        self.assertTrue(exe.is_valid())
 | 
				
			||||||
 | 
					        sys._clear_internal_caches()
 | 
				
			||||||
 | 
					        self.assertFalse(exe.is_valid())
 | 
				
			||||||
 | 
					        exe = get_first_executor(f)
 | 
				
			||||||
 | 
					        self.assertIsNone(exe)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestUops(unittest.TestCase):
 | 
					class TestUops(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_basic_loop(self):
 | 
					    def test_basic_loop(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@
 | 
				
			||||||
import tempfile
 | 
					import tempfile
 | 
				
			||||||
from test import support
 | 
					from test import support
 | 
				
			||||||
from test.support import os_helper
 | 
					from test.support import os_helper
 | 
				
			||||||
 | 
					from test.support import refleak_helper
 | 
				
			||||||
from test.support import socket_helper
 | 
					from test.support import socket_helper
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
import textwrap
 | 
					import textwrap
 | 
				
			||||||
| 
						 | 
					@ -2443,6 +2444,9 @@ def test__all__(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def tearDownModule():
 | 
					def tearDownModule():
 | 
				
			||||||
    support.reap_children()
 | 
					    support.reap_children()
 | 
				
			||||||
 | 
					    # reap_children may have re-populated caches:
 | 
				
			||||||
 | 
					    if refleak_helper.hunting_for_refleaks():
 | 
				
			||||||
 | 
					        sys._clear_internal_caches()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					Add :func:`sys._clear_internal_caches`, which clears all internal
 | 
				
			||||||
 | 
					performance-related caches (and deprecate the less-general
 | 
				
			||||||
 | 
					:func:`sys._clear_type_cache` function).
 | 
				
			||||||
| 
						 | 
					@ -1489,27 +1489,19 @@ PyCode_GetFreevars(PyCodeObject *code)
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
clear_executors(PyCodeObject *co)
 | 
					clear_executors(PyCodeObject *co)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    assert(co->co_executors);
 | 
				
			||||||
    for (int i = 0; i < co->co_executors->size; i++) {
 | 
					    for (int i = 0; i < co->co_executors->size; i++) {
 | 
				
			||||||
        Py_CLEAR(co->co_executors->executors[i]);
 | 
					        if (co->co_executors->executors[i]) {
 | 
				
			||||||
 | 
					            _Py_ExecutorClear(co->co_executors->executors[i]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    PyMem_Free(co->co_executors);
 | 
					    PyMem_Free(co->co_executors);
 | 
				
			||||||
    co->co_executors = NULL;
 | 
					    co->co_executors = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
_PyCode_Clear_Executors(PyCodeObject *code) {
 | 
					_PyCode_Clear_Executors(PyCodeObject *code)
 | 
				
			||||||
    int code_len = (int)Py_SIZE(code);
 | 
					{
 | 
				
			||||||
    for (int i = 0; i < code_len; i += _PyInstruction_GetLength(code, i)) {
 | 
					 | 
				
			||||||
        _Py_CODEUNIT *instr = &_PyCode_CODE(code)[i];
 | 
					 | 
				
			||||||
        uint8_t opcode = instr->op.code;
 | 
					 | 
				
			||||||
        uint8_t oparg = instr->op.arg;
 | 
					 | 
				
			||||||
        if (opcode == ENTER_EXECUTOR) {
 | 
					 | 
				
			||||||
            _PyExecutorObject *exec = code->co_executors->executors[oparg];
 | 
					 | 
				
			||||||
            assert(exec->vm_data.opcode != ENTER_EXECUTOR);
 | 
					 | 
				
			||||||
            instr->op.code = exec->vm_data.opcode;
 | 
					 | 
				
			||||||
            instr->op.arg = exec->vm_data.oparg;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    clear_executors(code);
 | 
					    clear_executors(code);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2360,10 +2352,10 @@ _PyCode_ConstantKey(PyObject *op)
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
_PyStaticCode_Fini(PyCodeObject *co)
 | 
					_PyStaticCode_Fini(PyCodeObject *co)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    deopt_code(co, _PyCode_CODE(co));
 | 
					 | 
				
			||||||
    if (co->co_executors != NULL) {
 | 
					    if (co->co_executors != NULL) {
 | 
				
			||||||
        clear_executors(co);
 | 
					        clear_executors(co);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    deopt_code(co, _PyCode_CODE(co));
 | 
				
			||||||
    PyMem_Free(co->co_extra);
 | 
					    PyMem_Free(co->co_extra);
 | 
				
			||||||
    if (co->_co_cached != NULL) {
 | 
					    if (co->_co_cached != NULL) {
 | 
				
			||||||
        Py_CLEAR(co->_co_cached->_co_code);
 | 
					        Py_CLEAR(co->_co_cached->_co_code);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2370,24 +2370,13 @@ dummy_func(
 | 
				
			||||||
            CHECK_EVAL_BREAKER();
 | 
					            CHECK_EVAL_BREAKER();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            PyCodeObject *code = _PyFrame_GetCode(frame);
 | 
					            PyCodeObject *code = _PyFrame_GetCode(frame);
 | 
				
			||||||
            _PyExecutorObject *executor = code->co_executors->executors[oparg & 255];
 | 
					            current_executor = code->co_executors->executors[oparg & 255];
 | 
				
			||||||
            if (executor->vm_data.valid) {
 | 
					            assert(current_executor->vm_data.index == INSTR_OFFSET() - 1);
 | 
				
			||||||
                Py_INCREF(executor);
 | 
					            assert(current_executor->vm_data.code == code);
 | 
				
			||||||
                current_executor = executor;
 | 
					            assert(current_executor->vm_data.valid);
 | 
				
			||||||
 | 
					            Py_INCREF(current_executor);
 | 
				
			||||||
            GOTO_TIER_TWO();
 | 
					            GOTO_TIER_TWO();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                /* ENTER_EXECUTOR will be the first code unit of the instruction */
 | 
					 | 
				
			||||||
                assert(oparg < 256);
 | 
					 | 
				
			||||||
                code->co_executors->executors[oparg] = NULL;
 | 
					 | 
				
			||||||
                opcode = this_instr->op.code = executor->vm_data.opcode;
 | 
					 | 
				
			||||||
                this_instr->op.arg = executor->vm_data.oparg;
 | 
					 | 
				
			||||||
                oparg = executor->vm_data.oparg;
 | 
					 | 
				
			||||||
                Py_DECREF(executor);
 | 
					 | 
				
			||||||
                next_instr = this_instr;
 | 
					 | 
				
			||||||
                DISPATCH_GOTO();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        replaced op(_POP_JUMP_IF_FALSE, (cond -- )) {
 | 
					        replaced op(_POP_JUMP_IF_FALSE, (cond -- )) {
 | 
				
			||||||
            assert(PyBool_Check(cond));
 | 
					            assert(PyBool_Check(cond));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										20
									
								
								Python/clinic/sysmodule.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								Python/clinic/sysmodule.c.h
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -1131,6 +1131,24 @@ sys__clear_type_cache(PyObject *module, PyObject *Py_UNUSED(ignored))
 | 
				
			||||||
    return sys__clear_type_cache_impl(module);
 | 
					    return sys__clear_type_cache_impl(module);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyDoc_STRVAR(sys__clear_internal_caches__doc__,
 | 
				
			||||||
 | 
					"_clear_internal_caches($module, /)\n"
 | 
				
			||||||
 | 
					"--\n"
 | 
				
			||||||
 | 
					"\n"
 | 
				
			||||||
 | 
					"Clear all internal performance-related caches.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SYS__CLEAR_INTERNAL_CACHES_METHODDEF    \
 | 
				
			||||||
 | 
					    {"_clear_internal_caches", (PyCFunction)sys__clear_internal_caches, METH_NOARGS, sys__clear_internal_caches__doc__},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					sys__clear_internal_caches_impl(PyObject *module);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					sys__clear_internal_caches(PyObject *module, PyObject *Py_UNUSED(ignored))
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return sys__clear_internal_caches_impl(module);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PyDoc_STRVAR(sys_is_finalizing__doc__,
 | 
					PyDoc_STRVAR(sys_is_finalizing__doc__,
 | 
				
			||||||
"is_finalizing($module, /)\n"
 | 
					"is_finalizing($module, /)\n"
 | 
				
			||||||
"--\n"
 | 
					"--\n"
 | 
				
			||||||
| 
						 | 
					@ -1486,4 +1504,4 @@ exit:
 | 
				
			||||||
#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
 | 
					#ifndef SYS_GETANDROIDAPILEVEL_METHODDEF
 | 
				
			||||||
    #define SYS_GETANDROIDAPILEVEL_METHODDEF
 | 
					    #define SYS_GETANDROIDAPILEVEL_METHODDEF
 | 
				
			||||||
#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
 | 
					#endif /* !defined(SYS_GETANDROIDAPILEVEL_METHODDEF) */
 | 
				
			||||||
/*[clinic end generated code: output=3dc3b2cb0ce38ebb input=a9049054013a1b77]*/
 | 
					/*[clinic end generated code: output=b8b1c53e04c3b20c input=a9049054013a1b77]*/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										23
									
								
								Python/generated_cases.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										23
									
								
								Python/generated_cases.c.h
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -2363,29 +2363,18 @@
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        TARGET(ENTER_EXECUTOR) {
 | 
					        TARGET(ENTER_EXECUTOR) {
 | 
				
			||||||
            _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr;
 | 
					            frame->instr_ptr = next_instr;
 | 
				
			||||||
            next_instr += 1;
 | 
					            next_instr += 1;
 | 
				
			||||||
            INSTRUCTION_STATS(ENTER_EXECUTOR);
 | 
					            INSTRUCTION_STATS(ENTER_EXECUTOR);
 | 
				
			||||||
            TIER_ONE_ONLY
 | 
					            TIER_ONE_ONLY
 | 
				
			||||||
            CHECK_EVAL_BREAKER();
 | 
					            CHECK_EVAL_BREAKER();
 | 
				
			||||||
            PyCodeObject *code = _PyFrame_GetCode(frame);
 | 
					            PyCodeObject *code = _PyFrame_GetCode(frame);
 | 
				
			||||||
            _PyExecutorObject *executor = code->co_executors->executors[oparg & 255];
 | 
					            current_executor = code->co_executors->executors[oparg & 255];
 | 
				
			||||||
            if (executor->vm_data.valid) {
 | 
					            assert(current_executor->vm_data.index == INSTR_OFFSET() - 1);
 | 
				
			||||||
                Py_INCREF(executor);
 | 
					            assert(current_executor->vm_data.code == code);
 | 
				
			||||||
                current_executor = executor;
 | 
					            assert(current_executor->vm_data.valid);
 | 
				
			||||||
 | 
					            Py_INCREF(current_executor);
 | 
				
			||||||
            GOTO_TIER_TWO();
 | 
					            GOTO_TIER_TWO();
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                /* ENTER_EXECUTOR will be the first code unit of the instruction */
 | 
					 | 
				
			||||||
                assert(oparg < 256);
 | 
					 | 
				
			||||||
                code->co_executors->executors[oparg] = NULL;
 | 
					 | 
				
			||||||
                opcode = this_instr->op.code = executor->vm_data.opcode;
 | 
					 | 
				
			||||||
                this_instr->op.arg = executor->vm_data.oparg;
 | 
					 | 
				
			||||||
                oparg = executor->vm_data.oparg;
 | 
					 | 
				
			||||||
                Py_DECREF(executor);
 | 
					 | 
				
			||||||
                next_instr = this_instr;
 | 
					 | 
				
			||||||
                DISPATCH_GOTO();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            DISPATCH();
 | 
					            DISPATCH();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,25 +73,21 @@ insert_executor(PyCodeObject *code, _Py_CODEUNIT *instr, int index, _PyExecutorO
 | 
				
			||||||
    Py_INCREF(executor);
 | 
					    Py_INCREF(executor);
 | 
				
			||||||
    if (instr->op.code == ENTER_EXECUTOR) {
 | 
					    if (instr->op.code == ENTER_EXECUTOR) {
 | 
				
			||||||
        assert(index == instr->op.arg);
 | 
					        assert(index == instr->op.arg);
 | 
				
			||||||
        _PyExecutorObject *old = code->co_executors->executors[index];
 | 
					        _Py_ExecutorClear(code->co_executors->executors[index]);
 | 
				
			||||||
        executor->vm_data.opcode = old->vm_data.opcode;
 | 
					 | 
				
			||||||
        executor->vm_data.oparg = old->vm_data.oparg;
 | 
					 | 
				
			||||||
        old->vm_data.opcode = 0;
 | 
					 | 
				
			||||||
        code->co_executors->executors[index] = executor;
 | 
					 | 
				
			||||||
        Py_DECREF(old);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
        assert(code->co_executors->size == index);
 | 
					        assert(code->co_executors->size == index);
 | 
				
			||||||
        assert(code->co_executors->capacity > index);
 | 
					        assert(code->co_executors->capacity > index);
 | 
				
			||||||
 | 
					        code->co_executors->size++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    executor->vm_data.opcode = instr->op.code;
 | 
					    executor->vm_data.opcode = instr->op.code;
 | 
				
			||||||
    executor->vm_data.oparg = instr->op.arg;
 | 
					    executor->vm_data.oparg = instr->op.arg;
 | 
				
			||||||
 | 
					    executor->vm_data.code = code;
 | 
				
			||||||
 | 
					    executor->vm_data.index = (int)(instr - _PyCode_CODE(code));
 | 
				
			||||||
    code->co_executors->executors[index] = executor;
 | 
					    code->co_executors->executors[index] = executor;
 | 
				
			||||||
    assert(index < MAX_EXECUTORS_SIZE);
 | 
					    assert(index < MAX_EXECUTORS_SIZE);
 | 
				
			||||||
    instr->op.code = ENTER_EXECUTOR;
 | 
					    instr->op.code = ENTER_EXECUTOR;
 | 
				
			||||||
    instr->op.arg = index;
 | 
					    instr->op.arg = index;
 | 
				
			||||||
        code->co_executors->size++;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
| 
						 | 
					@ -1071,7 +1067,7 @@ link_executor(_PyExecutorObject *executor)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        head->vm_data.links.next = executor;
 | 
					        head->vm_data.links.next = executor;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    executor->vm_data.linked = true;
 | 
					    executor->vm_data.valid = true;
 | 
				
			||||||
    /* executor_list_head must be first in list */
 | 
					    /* executor_list_head must be first in list */
 | 
				
			||||||
    assert(interp->executor_list_head->vm_data.links.previous == NULL);
 | 
					    assert(interp->executor_list_head->vm_data.links.previous == NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1079,7 +1075,7 @@ link_executor(_PyExecutorObject *executor)
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
unlink_executor(_PyExecutorObject *executor)
 | 
					unlink_executor(_PyExecutorObject *executor)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (!executor->vm_data.linked) {
 | 
					    if (!executor->vm_data.valid) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _PyExecutorLinkListNode *links = &executor->vm_data.links;
 | 
					    _PyExecutorLinkListNode *links = &executor->vm_data.links;
 | 
				
			||||||
| 
						 | 
					@ -1097,7 +1093,7 @@ unlink_executor(_PyExecutorObject *executor)
 | 
				
			||||||
        assert(interp->executor_list_head == executor);
 | 
					        assert(interp->executor_list_head == executor);
 | 
				
			||||||
        interp->executor_list_head = next;
 | 
					        interp->executor_list_head = next;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    executor->vm_data.linked = false;
 | 
					    executor->vm_data.valid = false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* This must be called by optimizers before using the executor */
 | 
					/* This must be called by optimizers before using the executor */
 | 
				
			||||||
| 
						 | 
					@ -1116,12 +1112,24 @@ void
 | 
				
			||||||
_Py_ExecutorClear(_PyExecutorObject *executor)
 | 
					_Py_ExecutorClear(_PyExecutorObject *executor)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    unlink_executor(executor);
 | 
					    unlink_executor(executor);
 | 
				
			||||||
 | 
					    PyCodeObject *code = executor->vm_data.code;
 | 
				
			||||||
 | 
					    if (code == NULL) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    _Py_CODEUNIT *instruction = &_PyCode_CODE(code)[executor->vm_data.index];
 | 
				
			||||||
 | 
					    assert(instruction->op.code == ENTER_EXECUTOR);
 | 
				
			||||||
 | 
					    int index = instruction->op.arg;
 | 
				
			||||||
 | 
					    assert(code->co_executors->executors[index] == executor);
 | 
				
			||||||
 | 
					    instruction->op.code = executor->vm_data.opcode;
 | 
				
			||||||
 | 
					    instruction->op.arg = executor->vm_data.oparg;
 | 
				
			||||||
 | 
					    executor->vm_data.code = NULL;
 | 
				
			||||||
 | 
					    Py_CLEAR(code->co_executors->executors[index]);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
_Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj)
 | 
					_Py_Executor_DependsOn(_PyExecutorObject *executor, void *obj)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    assert(executor->vm_data.valid = true);
 | 
					    assert(executor->vm_data.valid);
 | 
				
			||||||
    _Py_BloomFilter_Add(&executor->vm_data.bloom, obj);
 | 
					    _Py_BloomFilter_Add(&executor->vm_data.bloom, obj);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1140,8 +1148,7 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj)
 | 
				
			||||||
        assert(exec->vm_data.valid);
 | 
					        assert(exec->vm_data.valid);
 | 
				
			||||||
        _PyExecutorObject *next = exec->vm_data.links.next;
 | 
					        _PyExecutorObject *next = exec->vm_data.links.next;
 | 
				
			||||||
        if (bloom_filter_may_contain(&exec->vm_data.bloom, &obj_filter)) {
 | 
					        if (bloom_filter_may_contain(&exec->vm_data.bloom, &obj_filter)) {
 | 
				
			||||||
            exec->vm_data.valid = false;
 | 
					            _Py_ExecutorClear(exec);
 | 
				
			||||||
            unlink_executor(exec);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        exec = next;
 | 
					        exec = next;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -1151,15 +1158,14 @@ _Py_Executors_InvalidateDependency(PyInterpreterState *interp, void *obj)
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
_Py_Executors_InvalidateAll(PyInterpreterState *interp)
 | 
					_Py_Executors_InvalidateAll(PyInterpreterState *interp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /* Walk the list of executors */
 | 
					    while (interp->executor_list_head) {
 | 
				
			||||||
    for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) {
 | 
					        _PyExecutorObject *executor = interp->executor_list_head;
 | 
				
			||||||
        assert(exec->vm_data.valid);
 | 
					        if (executor->vm_data.code) {
 | 
				
			||||||
        _PyExecutorObject *next = exec->vm_data.links.next;
 | 
					            // Clear the entire code object so its co_executors array be freed:
 | 
				
			||||||
        exec->vm_data.links.next = NULL;
 | 
					            _PyCode_Clear_Executors(executor->vm_data.code);
 | 
				
			||||||
        exec->vm_data.links.previous = NULL;
 | 
					        }
 | 
				
			||||||
        exec->vm_data.valid = false;
 | 
					        else {
 | 
				
			||||||
        exec->vm_data.linked = false;
 | 
					            _Py_ExecutorClear(executor);
 | 
				
			||||||
        exec = next;
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    interp->executor_list_head = NULL;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2127,6 +2127,22 @@ sys__clear_type_cache_impl(PyObject *module)
 | 
				
			||||||
    Py_RETURN_NONE;
 | 
					    Py_RETURN_NONE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*[clinic input]
 | 
				
			||||||
 | 
					sys._clear_internal_caches
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Clear all internal performance-related caches.
 | 
				
			||||||
 | 
					[clinic start generated code]*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					sys__clear_internal_caches_impl(PyObject *module)
 | 
				
			||||||
 | 
					/*[clinic end generated code: output=0ee128670a4966d6 input=253e741ca744f6e8]*/
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PyInterpreterState *interp = _PyInterpreterState_GET();
 | 
				
			||||||
 | 
					    _Py_Executors_InvalidateAll(interp);
 | 
				
			||||||
 | 
					    PyType_ClearCache();
 | 
				
			||||||
 | 
					    Py_RETURN_NONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Note that, for now, we do not have a per-interpreter equivalent
 | 
					/* Note that, for now, we do not have a per-interpreter equivalent
 | 
				
			||||||
  for sys.is_finalizing(). */
 | 
					  for sys.is_finalizing(). */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2461,6 +2477,7 @@ static PyMethodDef sys_methods[] = {
 | 
				
			||||||
    {"audit", _PyCFunction_CAST(sys_audit), METH_FASTCALL, audit_doc },
 | 
					    {"audit", _PyCFunction_CAST(sys_audit), METH_FASTCALL, audit_doc },
 | 
				
			||||||
    {"breakpointhook", _PyCFunction_CAST(sys_breakpointhook),
 | 
					    {"breakpointhook", _PyCFunction_CAST(sys_breakpointhook),
 | 
				
			||||||
     METH_FASTCALL | METH_KEYWORDS, breakpointhook_doc},
 | 
					     METH_FASTCALL | METH_KEYWORDS, breakpointhook_doc},
 | 
				
			||||||
 | 
					    SYS__CLEAR_INTERNAL_CACHES_METHODDEF
 | 
				
			||||||
    SYS__CLEAR_TYPE_CACHE_METHODDEF
 | 
					    SYS__CLEAR_TYPE_CACHE_METHODDEF
 | 
				
			||||||
    SYS__CURRENT_FRAMES_METHODDEF
 | 
					    SYS__CURRENT_FRAMES_METHODDEF
 | 
				
			||||||
    SYS__CURRENT_EXCEPTIONS_METHODDEF
 | 
					    SYS__CURRENT_EXCEPTIONS_METHODDEF
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue