mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Split InterpreterFrame hot/cold fields into FrameColdData (#8434)
Move 10 rarely-used fields (trace, trace_lines, trace_opcodes, temporary_refs, f_locals_hidden_overlay, f_extra_locals, escaped, retained_back, pending_stack_pops, pending_unwind_from_stack) from InterpreterFrame into a lazily-allocated FrameColdData struct. InterpreterFrame now carries a single UnsafeCell<Option<Box<FrameColdData>>> (8 bytes) instead of ~200+ bytes of cold fields. The cold() accessor allocates on first access; frames that never trigger tracing or debugging pay no allocation cost. GC traversal skips cold data when it has not been allocated. Assisted-by: Claude
This commit is contained in:
1 parent
12f3646e7b
commit
9dff4d3a99
7 files changed
+154
-117
No files matched your search
@@ -636,8 +636,14 @@ impl FrameObject {
|
||||
} else {
|
||||
self.iframe()
|
||||
};
|
||||
target.pending_stack_pops.store(pop_count as u32, Relaxed);
|
||||
target.pending_unwind_from_stack.store(start_stack, Relaxed);
|
||||
target
|
||||
.cold()
|
||||
.pending_stack_pops
|
||||
.store(pop_count as u32, Relaxed);
|
||||
target
|
||||
.cold()
|
||||
.pending_unwind_from_stack
|
||||
.store(start_stack, Relaxed);
|
||||
target.lasti.store(best_addr as u32, Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
@@ -647,9 +653,9 @@ impl FrameObject {
|
||||
// Read from live source iframe if available.
|
||||
let live = self.find_live_source_iframe();
|
||||
let trace = if !live.is_null() {
|
||||
unsafe { &*live }.trace.lock().clone()
|
||||
unsafe { &*live }.cold().trace.lock().clone()
|
||||
} else {
|
||||
self.iframe().trace.lock().clone()
|
||||
self.iframe().cold().trace.lock().clone()
|
||||
};
|
||||
trace.unwrap_or_else(|| vm.ctx.none())
|
||||
}
|
||||
@@ -667,13 +673,13 @@ impl FrameObject {
|
||||
PySetterValue::Delete => None,
|
||||
};
|
||||
// Set on the materialized FrameObject.
|
||||
(*self.iframe().trace.lock()).clone_from(&trace);
|
||||
(*self.iframe().cold().trace.lock()).clone_from(&trace);
|
||||
// Also propagate to the live source iframe if this is a
|
||||
// materialized copy of a stack-allocated frame, so pdb's
|
||||
// f_trace assignment takes effect on the executing frame.
|
||||
let live = self.find_live_source_iframe();
|
||||
if !live.is_null() {
|
||||
*unsafe { &*live }.trace.lock() = trace;
|
||||
*unsafe { &*live }.cold().trace.lock() = trace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,7 +688,7 @@ impl FrameObject {
|
||||
fn f_trace_lines(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
|
||||
let zelf: FrameObjectRef = zelf.downcast().unwrap_or_else(|_| unreachable!());
|
||||
|
||||
let boxed = zelf.iframe().trace_lines.lock();
|
||||
let boxed = zelf.iframe().cold().trace_lines.lock();
|
||||
Ok(vm.ctx.new_bool(*boxed).into())
|
||||
}
|
||||
|
||||
@@ -701,11 +707,11 @@ impl FrameObject {
|
||||
.map_err(|_| vm.new_type_error("attribute value type must be bool"))?;
|
||||
|
||||
let val = !value.as_bigint().is_zero();
|
||||
*zelf.iframe().trace_lines.lock() = val;
|
||||
*zelf.iframe().cold().trace_lines.lock() = val;
|
||||
// Propagate to live source iframe.
|
||||
let live = zelf.find_live_source_iframe();
|
||||
if !live.is_null() {
|
||||
*unsafe { &*live }.trace_lines.lock() = val;
|
||||
*unsafe { &*live }.cold().trace_lines.lock() = val;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -718,7 +724,7 @@ impl FrameObject {
|
||||
#[pymember(type = "bool")]
|
||||
fn f_trace_opcodes(vm: &VirtualMachine, zelf: PyObjectRef) -> PyResult {
|
||||
let zelf: FrameObjectRef = zelf.downcast().unwrap_or_else(|_| unreachable!());
|
||||
let trace_opcodes = zelf.iframe().trace_opcodes.lock();
|
||||
let trace_opcodes = zelf.iframe().cold().trace_opcodes.lock();
|
||||
Ok(vm.ctx.new_bool(*trace_opcodes).into())
|
||||
}
|
||||
|
||||
@@ -737,11 +743,11 @@ impl FrameObject {
|
||||
.map_err(|_| vm.new_type_error("attribute value type must be bool"))?;
|
||||
|
||||
let val = !value.as_bigint().is_zero();
|
||||
*zelf.iframe().trace_opcodes.lock() = val;
|
||||
*zelf.iframe().cold().trace_opcodes.lock() = val;
|
||||
// Propagate to live source iframe.
|
||||
let live = zelf.find_live_source_iframe();
|
||||
if !live.is_null() {
|
||||
*unsafe { &*live }.trace_opcodes.lock() = val;
|
||||
*unsafe { &*live }.cold().trace_opcodes.lock() = val;
|
||||
}
|
||||
|
||||
// TODO: Implement the equivalent of _PyEval_SetOpcodeTrace()
|
||||
@@ -798,10 +804,10 @@ impl Py<FrameObject> {
|
||||
self.clear_stack_and_cells();
|
||||
|
||||
// Clear temporary refs
|
||||
self.iframe().temporary_refs.lock().clear();
|
||||
self.iframe().f_locals_hidden_overlay.lock().take();
|
||||
self.iframe().f_extra_locals.lock().take();
|
||||
self.iframe().retained_back.lock().take();
|
||||
self.iframe().cold().temporary_refs.lock().clear();
|
||||
self.iframe().cold().f_locals_hidden_overlay.lock().take();
|
||||
self.iframe().cold().f_extra_locals.lock().take();
|
||||
self.iframe().cold().retained_back.lock().take();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -853,7 +859,7 @@ impl Py<FrameObject> {
|
||||
}
|
||||
if prev.is_null() {
|
||||
// Check retained_back for frames whose callers have returned
|
||||
let retained = self.iframe().retained_back.lock().clone();
|
||||
let retained = self.iframe().cold().retained_back.lock().clone();
|
||||
if let Some(frame) = retained {
|
||||
frame.mark_escaped();
|
||||
return Some(frame);
|
||||
@@ -879,7 +885,7 @@ impl Py<FrameObject> {
|
||||
}
|
||||
|
||||
// The caller already returned — check retained_back
|
||||
let retained = self.iframe().retained_back.lock().clone();
|
||||
let retained = self.iframe().cold().retained_back.lock().clone();
|
||||
if let Some(frame) = retained {
|
||||
frame.mark_escaped();
|
||||
return Some(frame);
|
||||
@@ -906,7 +912,7 @@ impl Py<FrameObject> {
|
||||
let iframe = unsafe { &*cur };
|
||||
let fo = iframe.materialize(vm).to_owned();
|
||||
if let Some(child) = child_fo.take() {
|
||||
let mut guard = child.iframe().retained_back.lock();
|
||||
let mut guard = child.iframe().cold().retained_back.lock();
|
||||
if guard.is_none() {
|
||||
*guard = Some(fo.clone());
|
||||
}
|
||||
|
||||
@@ -1513,7 +1513,12 @@ impl PyType {
|
||||
// temporary refs so they never see a dangling pointer.
|
||||
let keep_alive = |type_ref: PyTypeRef, retired: &mut Vec<PyObjectRef>| {
|
||||
if let Some(frame) = vm.current_frame() {
|
||||
frame.iframe().temporary_refs.lock().push(type_ref.into());
|
||||
frame
|
||||
.iframe()
|
||||
.cold()
|
||||
.temporary_refs
|
||||
.lock()
|
||||
.push(type_ref.into());
|
||||
} else {
|
||||
retired.push(type_ref.into());
|
||||
}
|
||||
|
||||
+101
-84
@@ -792,6 +792,39 @@ unsafe impl Traverse for FrameLocals {
|
||||
}
|
||||
}
|
||||
|
||||
/// Cold fields of InterpreterFrame that are only accessed during tracing,
|
||||
/// debugging, frame inspection, or GC. Lazily allocated on first access
|
||||
/// to keep the hot InterpreterFrame small.
|
||||
pub(crate) struct FrameColdData {
|
||||
pub trace: PyMutex<Option<PyObjectRef>>,
|
||||
pub trace_lines: PyMutex<bool>,
|
||||
pub trace_opcodes: PyMutex<bool>,
|
||||
pub temporary_refs: PyMutex<Vec<PyObjectRef>>,
|
||||
pub f_locals_hidden_overlay: PyMutex<Option<PyDictRef>>,
|
||||
pub f_extra_locals: PyMutex<Option<PyDictRef>>,
|
||||
pub escaped: atomic::AtomicBool,
|
||||
pub retained_back: PyMutex<Option<FrameObjectRef>>,
|
||||
pub pending_stack_pops: PyAtomic<u32>,
|
||||
pub pending_unwind_from_stack: PyAtomic<i64>,
|
||||
}
|
||||
|
||||
impl Default for FrameColdData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
trace: PyMutex::new(None),
|
||||
trace_lines: PyMutex::new(true),
|
||||
trace_opcodes: PyMutex::new(false),
|
||||
temporary_refs: PyMutex::new(Vec::new()),
|
||||
f_locals_hidden_overlay: PyMutex::new(None),
|
||||
f_extra_locals: PyMutex::new(None),
|
||||
escaped: atomic::AtomicBool::new(false),
|
||||
retained_back: PyMutex::new(None),
|
||||
pending_stack_pops: Default::default(),
|
||||
pending_unwind_from_stack: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lightweight execution frame. Not a PyObject.
|
||||
/// Analogous to CPython's `_PyInterpreterFrame`.
|
||||
///
|
||||
@@ -813,18 +846,10 @@ pub struct InterpreterFrame {
|
||||
|
||||
/// index of last instruction ran
|
||||
pub lasti: PyAtomic<u32>,
|
||||
/// Per-frame tracer function. `None` means no per-frame trace is set
|
||||
/// (equivalent to `f_trace = None` in Python). This avoids a refcounted
|
||||
/// None clone on every frame init.
|
||||
pub trace: PyMutex<Option<PyObjectRef>>,
|
||||
|
||||
/// Previous line number for LINE event suppression.
|
||||
pub(crate) prev_line: core::cell::Cell<u32>,
|
||||
|
||||
// member
|
||||
pub trace_lines: PyMutex<bool>,
|
||||
pub trace_opcodes: PyMutex<bool>,
|
||||
pub temporary_refs: PyMutex<Vec<PyObjectRef>>,
|
||||
/// Back-reference to owning generator/coroutine/async generator.
|
||||
/// Borrowed reference (not ref-counted) to avoid Generator↔FrameObject cycle.
|
||||
/// Cleared by the generator's Drop impl.
|
||||
@@ -836,32 +861,14 @@ pub struct InterpreterFrame {
|
||||
/// Used by `frame.clear()` to reject clearing an executing frame,
|
||||
/// even when called from a different thread.
|
||||
pub(crate) owner: atomic::AtomicI8,
|
||||
/// Persistent overlay for `frame.f_locals` when hidden locals need a
|
||||
/// snapshot separate from the backing locals mapping.
|
||||
pub(crate) f_locals_hidden_overlay: PyMutex<Option<PyDictRef>>,
|
||||
/// Side storage for `f_locals` proxy keys that do not name a fast local.
|
||||
/// Lazily created on first non-fast-key write. Mirrors `f_extra_locals`.
|
||||
pub(crate) f_extra_locals: PyMutex<Option<PyDictRef>>,
|
||||
/// Set once a durable Python-level reference to this frame is handed out
|
||||
/// (`f_locals` proxy, `sys._getframe`, `f_back`). A closed generator keeps
|
||||
/// its locals alive while this is set, mirroring `frame_obj` ownership.
|
||||
pub(crate) escaped: atomic::AtomicBool,
|
||||
/// Strong reference to the caller frame, captured when this frame escapes
|
||||
/// its execution so `f_back` still resolves after the caller returns and
|
||||
/// leaves the live frame chain.
|
||||
pub(crate) retained_back: PyMutex<Option<FrameObjectRef>>,
|
||||
/// Number of stack entries to pop after set_f_lineno returns to the
|
||||
/// execution loop. set_f_lineno cannot pop directly because the
|
||||
/// execution loop holds the state mutex.
|
||||
pub(crate) pending_stack_pops: PyAtomic<u32>,
|
||||
/// The encoded stack state that set_f_lineno wants to unwind *from*.
|
||||
/// Used together with `pending_stack_pops` to identify Except entries
|
||||
/// that need special exception-state handling.
|
||||
pub(crate) pending_unwind_from_stack: PyAtomic<i64>,
|
||||
/// Pointer to the owning `Py<FrameObject>`, or null for stack-allocated
|
||||
/// frames that have not been materialized yet.
|
||||
/// Stored as `usize` for `PyAtomic` compatibility.
|
||||
pub(crate) materialized: PyAtomic<usize>,
|
||||
|
||||
/// Lazily-allocated cold data (tracing, debugging, frame inspection).
|
||||
/// Not allocated until first access via `cold()`.
|
||||
pub(crate) cold: OnceCell<Box<FrameColdData>>,
|
||||
}
|
||||
|
||||
// Raw pointers make InterpreterFrame !Send+!Sync by default.
|
||||
@@ -934,20 +941,11 @@ impl InterpreterFrame {
|
||||
locals,
|
||||
lasti: Radium::new(0),
|
||||
prev_line: core::cell::Cell::new(prev_line),
|
||||
trace: PyMutex::new(None),
|
||||
trace_lines: PyMutex::new(true),
|
||||
trace_opcodes: PyMutex::new(false),
|
||||
temporary_refs: PyMutex::new(vec![]),
|
||||
generator: PyAtomicBorrow::new(),
|
||||
previous: Radium::new(0),
|
||||
owner: atomic::AtomicI8::new(owner as i8),
|
||||
f_locals_hidden_overlay: PyMutex::new(None),
|
||||
f_extra_locals: PyMutex::new(None),
|
||||
escaped: atomic::AtomicBool::new(false),
|
||||
retained_back: PyMutex::new(None),
|
||||
pending_stack_pops: Default::default(),
|
||||
pending_unwind_from_stack: Default::default(),
|
||||
materialized: Radium::new(0),
|
||||
cold: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,10 +1029,6 @@ impl InterpreterFrame {
|
||||
locals,
|
||||
lasti: Radium::new(self.lasti.load(Relaxed)),
|
||||
prev_line: core::cell::Cell::new(self.prev_line.get()),
|
||||
trace: PyMutex::new(None),
|
||||
trace_lines: PyMutex::new(true),
|
||||
trace_opcodes: PyMutex::new(false),
|
||||
temporary_refs: PyMutex::new(vec![]),
|
||||
generator: PyAtomicBorrow::new(),
|
||||
// Do NOT copy previous — it may point to stack-allocated frames
|
||||
// that become dangling after their call returns. The f_back chain
|
||||
@@ -1044,13 +1038,11 @@ impl InterpreterFrame {
|
||||
// If we copied Thread from the source iframe, frame.clear() would
|
||||
// reject the frame with "cannot clear an executing frame".
|
||||
owner: atomic::AtomicI8::new(FrameOwner::FrameObject as i8),
|
||||
f_locals_hidden_overlay: PyMutex::new(None),
|
||||
f_extra_locals: PyMutex::new(None),
|
||||
escaped: atomic::AtomicBool::new(true),
|
||||
retained_back: PyMutex::new(None),
|
||||
pending_stack_pops: Default::default(),
|
||||
pending_unwind_from_stack: Default::default(),
|
||||
materialized: Radium::new(0),
|
||||
cold: OnceCell::from(Box::new(FrameColdData {
|
||||
escaped: atomic::AtomicBool::new(true),
|
||||
..FrameColdData::default()
|
||||
})),
|
||||
};
|
||||
|
||||
let frame_obj = FrameObject {
|
||||
@@ -1079,7 +1071,10 @@ impl InterpreterFrame {
|
||||
// is no longer executing and temporary_refs is cleared — at that
|
||||
// point the FrameObject is self-sustaining and GC can safely
|
||||
// traverse and collect it.
|
||||
self.temporary_refs.lock().push(frame_ref.clone().into());
|
||||
self.cold()
|
||||
.temporary_refs
|
||||
.lock()
|
||||
.push(frame_ref.clone().into());
|
||||
|
||||
// SAFETY: the pointer we stored above remains valid because
|
||||
// temporary_refs holds a strong reference.
|
||||
@@ -1119,20 +1114,14 @@ impl InterpreterFrame {
|
||||
locals,
|
||||
lasti: Radium::new(self.lasti.load(Relaxed)),
|
||||
prev_line: core::cell::Cell::new(self.prev_line.get()),
|
||||
trace: PyMutex::new(None),
|
||||
trace_lines: PyMutex::new(true),
|
||||
trace_opcodes: PyMutex::new(false),
|
||||
temporary_refs: PyMutex::new(vec![]),
|
||||
generator: PyAtomicBorrow::new(),
|
||||
previous: Radium::new(0),
|
||||
owner: atomic::AtomicI8::new(FrameOwner::FrameObject as i8),
|
||||
f_locals_hidden_overlay: PyMutex::new(None),
|
||||
f_extra_locals: PyMutex::new(None),
|
||||
escaped: atomic::AtomicBool::new(true),
|
||||
retained_back: PyMutex::new(None),
|
||||
pending_stack_pops: Default::default(),
|
||||
pending_unwind_from_stack: Default::default(),
|
||||
materialized: Radium::new(0),
|
||||
cold: OnceCell::from(Box::new(FrameColdData {
|
||||
escaped: atomic::AtomicBool::new(true),
|
||||
..FrameColdData::default()
|
||||
})),
|
||||
};
|
||||
|
||||
let frame_obj = FrameObject {
|
||||
@@ -1175,6 +1164,19 @@ impl InterpreterFrame {
|
||||
Some(unsafe { &*self.func_obj })
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the lazily-allocated cold data, allocating on first use.
|
||||
#[inline]
|
||||
pub(crate) fn cold(&self) -> &FrameColdData {
|
||||
self.cold.get_or_init(|| Box::new(FrameColdData::default()))
|
||||
}
|
||||
|
||||
/// Access cold data without allocating. Returns `None` if cold data
|
||||
/// has not been allocated yet.
|
||||
#[inline]
|
||||
pub(crate) fn cold_opt(&self) -> Option<&FrameColdData> {
|
||||
self.cold.get().map(|b| &**b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Python-visible frame object. Currently always wraps an `InterpreterFrame`.
|
||||
@@ -1323,11 +1325,13 @@ unsafe impl Traverse for FrameObject {
|
||||
};
|
||||
iframe.localsplus.traverse(tracer_fn);
|
||||
iframe.locals.traverse(tracer_fn);
|
||||
iframe.trace.traverse(tracer_fn);
|
||||
iframe.temporary_refs.traverse(tracer_fn);
|
||||
iframe.f_locals_hidden_overlay.traverse(tracer_fn);
|
||||
iframe.f_extra_locals.traverse(tracer_fn);
|
||||
iframe.retained_back.traverse(tracer_fn);
|
||||
if let Some(cold) = iframe.cold_opt() {
|
||||
cold.trace.traverse(tracer_fn);
|
||||
cold.temporary_refs.traverse(tracer_fn);
|
||||
cold.f_locals_hidden_overlay.traverse(tracer_fn);
|
||||
cold.f_extra_locals.traverse(tracer_fn);
|
||||
cold.retained_back.traverse(tracer_fn);
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self, _out: &mut Vec<PyObjectRef>) {
|
||||
@@ -1505,8 +1509,8 @@ impl FrameObject {
|
||||
for slot in fastlocals.iter_mut() {
|
||||
*slot = None;
|
||||
}
|
||||
self.iframe().f_locals_hidden_overlay.lock().take();
|
||||
self.iframe().f_extra_locals.lock().take();
|
||||
self.iframe().cold().f_locals_hidden_overlay.lock().take();
|
||||
self.iframe().cold().f_extra_locals.lock().take();
|
||||
}
|
||||
|
||||
/// Store a borrowed back-reference to the owning generator/coroutine.
|
||||
@@ -1578,12 +1582,17 @@ impl FrameObject {
|
||||
|
||||
/// Record that a durable Python-level reference to this frame escaped.
|
||||
pub(crate) fn mark_escaped(&self) {
|
||||
self.iframe().escaped.store(true, atomic::Ordering::Release);
|
||||
self.iframe()
|
||||
.cold()
|
||||
.escaped
|
||||
.store(true, atomic::Ordering::Release);
|
||||
}
|
||||
|
||||
/// Whether a durable reference to this frame has escaped.
|
||||
pub(crate) fn has_escaped(&self) -> bool {
|
||||
self.iframe().escaped.load(atomic::Ordering::Acquire)
|
||||
self.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| c.escaped.load(atomic::Ordering::Acquire))
|
||||
}
|
||||
|
||||
pub fn lasti(&self) -> u32 {
|
||||
@@ -1769,12 +1778,12 @@ impl FrameObject {
|
||||
pub fn f_locals_mapping(&self, vm: &VirtualMachine) -> PyResult<ArgMapping> {
|
||||
self.check_locals_access(vm)?;
|
||||
if !self.has_active_hidden_locals() {
|
||||
self.iframe().f_locals_hidden_overlay.lock().take();
|
||||
self.iframe().cold().f_locals_hidden_overlay.lock().take();
|
||||
return self.locals(vm);
|
||||
}
|
||||
|
||||
let overlay_dict = {
|
||||
let mut overlay = self.iframe().f_locals_hidden_overlay.lock();
|
||||
let mut overlay = self.iframe().cold().f_locals_hidden_overlay.lock();
|
||||
match overlay.as_ref() {
|
||||
Some(dict) => dict.clone(),
|
||||
None => {
|
||||
@@ -1808,7 +1817,7 @@ impl FrameObject {
|
||||
/// Copy the frame's extra-locals side storage (proxy keys that are not
|
||||
/// fast locals) into `mapping`. No-op when nothing was ever stored.
|
||||
fn fold_extra_locals(&self, mapping: &ArgMapping, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let extra = self.iframe().f_extra_locals.lock().clone();
|
||||
let extra = self.iframe().cold().f_extra_locals.lock().clone();
|
||||
if let Some(extra) = extra {
|
||||
for (key, value) in &extra {
|
||||
mapping.mapping().ass_subscript(&key, Some(value), vm)?;
|
||||
@@ -1934,7 +1943,7 @@ impl FrameObject {
|
||||
{
|
||||
return Ok(value);
|
||||
}
|
||||
let extra = self.iframe().f_extra_locals.lock().clone();
|
||||
let extra = self.iframe().cold().f_extra_locals.lock().clone();
|
||||
if let Some(extra) = extra
|
||||
&& let Some(value) = extra.get_item_opt(&*key, vm)?
|
||||
{
|
||||
@@ -1953,7 +1962,7 @@ impl FrameObject {
|
||||
if self.framelocalsproxy_getkeyindex(&key, true, vm)?.is_some() {
|
||||
return Ok(true);
|
||||
}
|
||||
let extra = self.iframe().f_extra_locals.lock().clone();
|
||||
let extra = self.iframe().cold().f_extra_locals.lock().clone();
|
||||
if let Some(extra) = extra {
|
||||
return Ok(extra.get_item_opt(&*key, vm)?.is_some());
|
||||
}
|
||||
@@ -1991,7 +2000,7 @@ impl FrameObject {
|
||||
{
|
||||
return Err(vm.new_value_error("cannot remove local variables from FrameLocalsProxy"));
|
||||
}
|
||||
let extra = self.iframe().f_extra_locals.lock().clone();
|
||||
let extra = self.iframe().cold().f_extra_locals.lock().clone();
|
||||
if let Some(extra) = extra
|
||||
&& extra.get_item_opt(&*key, vm)?.is_some()
|
||||
{
|
||||
@@ -2014,7 +2023,7 @@ impl FrameObject {
|
||||
{
|
||||
return Err(vm.new_value_error("cannot remove local variables from FrameLocalsProxy"));
|
||||
}
|
||||
let extra = self.iframe().f_extra_locals.lock().clone();
|
||||
let extra = self.iframe().cold().f_extra_locals.lock().clone();
|
||||
if let Some(extra) = extra
|
||||
&& let Some(value) = extra.pop_item(&*key, vm)?
|
||||
{
|
||||
@@ -2041,7 +2050,7 @@ impl FrameObject {
|
||||
}
|
||||
|
||||
fn extra_locals_get_or_create(&self, vm: &VirtualMachine) -> PyDictRef {
|
||||
let mut extra = self.iframe().f_extra_locals.lock();
|
||||
let mut extra = self.iframe().cold().f_extra_locals.lock();
|
||||
extra.get_or_insert_with(|| vm.ctx.new_dict()).clone()
|
||||
}
|
||||
}
|
||||
@@ -2372,7 +2381,7 @@ pub(crate) fn release_datastack_frame(frame: &Py<FrameObject>, vm: &VirtualMachi
|
||||
// executing here (this frame is unwinding back into it), so its payload
|
||||
// pointer is live.
|
||||
{
|
||||
let mut guard = frame.iframe().retained_back.lock();
|
||||
let mut guard = frame.iframe().cold().retained_back.lock();
|
||||
if guard.is_none() {
|
||||
let prev = frame.previous_iframe();
|
||||
*guard = unsafe { owned_chain_frame(prev) };
|
||||
@@ -2613,31 +2622,39 @@ impl ExecutingFrame<'_> {
|
||||
/// Whether this frame has a per-frame trace function set.
|
||||
#[inline]
|
||||
fn trace_is_set(&self, _vm: &VirtualMachine) -> bool {
|
||||
self.iframe().trace.lock().is_some()
|
||||
self.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| c.trace.lock().is_some())
|
||||
}
|
||||
|
||||
/// Access the frame's trace_opcodes lock.
|
||||
#[inline]
|
||||
fn trace_opcodes_is_set(&self) -> bool {
|
||||
*self.iframe().trace_opcodes.lock()
|
||||
self.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| *c.trace_opcodes.lock())
|
||||
}
|
||||
|
||||
/// Get pending_stack_pops from the frame.
|
||||
#[inline]
|
||||
fn pending_stack_pops(&self) -> u32 {
|
||||
self.iframe().pending_stack_pops.load(Relaxed)
|
||||
self.iframe()
|
||||
.cold_opt()
|
||||
.map_or(0, |c| c.pending_stack_pops.load(Relaxed))
|
||||
}
|
||||
|
||||
/// Get pending_unwind_from_stack from the frame.
|
||||
#[inline]
|
||||
fn pending_unwind_from_stack(&self) -> i64 {
|
||||
self.iframe().pending_unwind_from_stack.load(Relaxed)
|
||||
self.iframe()
|
||||
.cold_opt()
|
||||
.map_or(0, |c| c.pending_unwind_from_stack.load(Relaxed))
|
||||
}
|
||||
|
||||
/// Set pending_stack_pops on the frame.
|
||||
#[inline]
|
||||
fn set_pending_stack_pops(&self, val: u32) {
|
||||
self.iframe().pending_stack_pops.store(val, Relaxed);
|
||||
self.iframe().cold().pending_stack_pops.store(val, Relaxed);
|
||||
}
|
||||
|
||||
/// Run `__init__` for the tp_new specialization. `args` holds the
|
||||
|
||||
@@ -333,7 +333,7 @@ impl<T: PyPayload> PyAtomicRef<T> {
|
||||
pub fn swap_to_temporary_refs(&self, pyref: PyRef<T>, vm: &VirtualMachine) {
|
||||
let old = unsafe { self.swap(pyref) };
|
||||
if let Some(frame) = vm.current_frame() {
|
||||
frame.iframe().temporary_refs.lock().push(old.into());
|
||||
frame.iframe().cold().temporary_refs.lock().push(old.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -409,7 +409,7 @@ impl<T: PyPayload> PyAtomicRef<Option<T>> {
|
||||
return;
|
||||
};
|
||||
if let Some(frame) = vm.current_frame() {
|
||||
frame.iframe().temporary_refs.lock().push(old.into());
|
||||
frame.iframe().cold().temporary_refs.lock().push(old.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -452,7 +452,7 @@ impl PyAtomicRef<PyObject> {
|
||||
pub fn swap_to_temporary_refs(&self, obj: PyObjectRef, vm: &VirtualMachine) {
|
||||
let old = unsafe { self.swap(obj) };
|
||||
if let Some(frame) = vm.current_frame() {
|
||||
frame.iframe().temporary_refs.lock().push(old);
|
||||
frame.iframe().cold().temporary_refs.lock().push(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -499,7 +499,7 @@ impl PyAtomicRef<Option<PyObject>> {
|
||||
return;
|
||||
};
|
||||
if let Some(frame) = vm.current_frame() {
|
||||
frame.iframe().temporary_refs.lock().push(old);
|
||||
frame.iframe().cold().temporary_refs.lock().push(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +233,12 @@ impl VirtualMachine {
|
||||
};
|
||||
|
||||
// Opcode events are only dispatched when f_trace_opcodes is set.
|
||||
if is_opcode_event && !*frame_ref.iframe().trace_opcodes.lock() {
|
||||
if is_opcode_event
|
||||
&& !frame_ref
|
||||
.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| *c.trace_opcodes.lock())
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -261,7 +266,7 @@ impl VirtualMachine {
|
||||
// trace_trampoline behavior: clear per-frame f_trace
|
||||
// and propagate the error.
|
||||
if let Some(frame_ref) = self.current_frame() {
|
||||
*frame_ref.iframe().trace.lock() = None;
|
||||
*frame_ref.iframe().cold().trace.lock() = None;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
@@ -1206,7 +1206,7 @@ pub(crate) mod _thread {
|
||||
let iframe = unsafe { &*cur };
|
||||
let fo = iframe.materialize(vm).to_owned();
|
||||
if let Some(child) = child_fo.take() {
|
||||
let mut guard = child.iframe().retained_back.lock();
|
||||
let mut guard = child.iframe().cold().retained_back.lock();
|
||||
if guard.is_none() {
|
||||
*guard = Some(fo.clone());
|
||||
}
|
||||
@@ -1253,7 +1253,7 @@ pub(crate) mod _thread {
|
||||
let iframe = unsafe { &*cur };
|
||||
let fo = iframe.materialize(vm).to_owned();
|
||||
if let Some(child) = child_fo.take() {
|
||||
let mut guard = child.iframe().retained_back.lock();
|
||||
let mut guard = child.iframe().cold().retained_back.lock();
|
||||
if guard.is_none() {
|
||||
*guard = Some(fo.clone());
|
||||
}
|
||||
|
||||
@@ -1768,7 +1768,7 @@ impl VirtualMachine {
|
||||
// materialized, f_back will resolve via the TLS chain while the
|
||||
// caller is still executing, or return None after it returns.
|
||||
if strong > 1 {
|
||||
let mut guard = frame.iframe().retained_back.lock();
|
||||
let mut guard = frame.iframe().cold().retained_back.lock();
|
||||
if guard.is_none() {
|
||||
let prev_iframe = unsafe { &*old_chain };
|
||||
if let Some(fo) = prev_iframe.frame_obj() {
|
||||
@@ -1884,7 +1884,7 @@ impl VirtualMachine {
|
||||
// lightweight frame has empty localsplus; live values are
|
||||
// read through find_live_source_iframe when needed.
|
||||
let back_fo = prev_iframe.materialize_chain(self);
|
||||
*fo.iframe().retained_back.lock() = Some(back_fo);
|
||||
*fo.iframe().cold().retained_back.lock() = Some(back_fo);
|
||||
}
|
||||
// Set owner to FrameObject since this frame is no longer
|
||||
// executing on a thread.
|
||||
@@ -1918,7 +1918,7 @@ impl VirtualMachine {
|
||||
crate::gc_state::gc_state()
|
||||
.track_object(core::ptr::NonNull::from(fo.as_object()));
|
||||
let live_iframe = &*iframe_ptr;
|
||||
live_iframe.temporary_refs.lock().clear();
|
||||
live_iframe.cold().temporary_refs.lock().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2002,7 +2002,7 @@ impl VirtualMachine {
|
||||
// Fire 'call' trace event. current_frame() now returns the callee.
|
||||
let trace_result = self.trace_event(TraceEvent::Call, None)?;
|
||||
if let Some(local_trace) = trace_result {
|
||||
*frame.iframe().trace.lock() = Some(local_trace);
|
||||
*frame.iframe().cold().trace.lock() = Some(local_trace);
|
||||
}
|
||||
|
||||
let result = f(frame);
|
||||
@@ -2011,7 +2011,11 @@ impl VirtualMachine {
|
||||
// PY_UNWIND fires PyTrace_RETURN with arg=None — so we fire for
|
||||
// both Ok and Err, matching `call_trace_protected` behavior.
|
||||
if self.use_tracing.get()
|
||||
&& (frame.iframe().trace.lock().is_some() || !self.is_none(&self.profile_func.borrow()))
|
||||
&& (!self.is_none(&self.profile_func.borrow())
|
||||
|| frame
|
||||
.iframe()
|
||||
.cold_opt()
|
||||
.is_some_and(|c| c.trace.lock().is_some()))
|
||||
{
|
||||
let ret_result = self.trace_event(TraceEvent::Return, None);
|
||||
// call_trace_protected: if trace function raises, its error
|
||||
|
||||
Reference in new issue
Block a user