Skip to content

Commit d22fddd

Browse files
committed
Give trampoline frames a unique non-Copy handle
run_trampoline takes &mut again; the loop holds TrampolineIFrame so two &mut InterpreterFrame cannot alias the same allocation. Assisted-by: Grok:4.6
1 parent 8035209 commit d22fddd

1 file changed

Lines changed: 73 additions & 37 deletions

File tree

‎crates/vm/src/vm/mod.rs‎

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,9 +1003,38 @@ impl FrameKind {
10031003
}
10041004
}
10051005

1006+
/// Unique right to mutably run an interpreter frame in the trampoline.
1007+
///
1008+
/// Not `Copy`: two handles to the same allocation would let two
1009+
/// `&mut InterpreterFrame` exist at once. `from_mut` consumes an exclusive
1010+
/// borrow; `from_ptr` is `unsafe` and must not alias another live handle.
1011+
/// Pointer validity (datastack LIFO, generator `PyRef` + running claim) is
1012+
/// still a construction contract, not something this type can prove.
1013+
struct TrampolineIFrame {
1014+
ptr: *mut crate::frame::InterpreterFrame,
1015+
}
1016+
1017+
impl TrampolineIFrame {
1018+
fn from_mut(iframe: &mut crate::frame::InterpreterFrame) -> Self {
1019+
Self { ptr: iframe }
1020+
}
1021+
1022+
/// # Safety
1023+
/// `ptr` must point to a live frame, and no other `TrampolineIFrame`
1024+
/// may alias it until this handle is dropped.
1025+
unsafe fn from_ptr(ptr: *mut crate::frame::InterpreterFrame) -> Self {
1026+
Self { ptr }
1027+
}
1028+
1029+
fn as_mut(&mut self) -> &mut crate::frame::InterpreterFrame {
1030+
// SAFETY: unique handle; construction established the pointer.
1031+
unsafe { &mut *self.ptr }
1032+
}
1033+
}
1034+
10061035
/// Caller frame suspended by a TailCall in the trampoline.
10071036
struct SuspendedFrame {
1008-
iframe: *mut crate::frame::InterpreterFrame,
1037+
iframe: TrampolineIFrame,
10091038
kind: FrameKind,
10101039
/// Function that owns the callee's raw pointers (code, globals, builtins,
10111040
/// closure, and func_obj). Moved from `vm.pending_tailcall_owner` when the
@@ -1057,7 +1086,7 @@ enum GenEntered {
10571086
/// The generator at the bottom of the chain ran and produced `result`;
10581087
/// `state` and `iframe` are its own.
10591088
Ran {
1060-
iframe: *mut crate::frame::InterpreterFrame,
1089+
iframe: TrampolineIFrame,
10611090
state: crate::coroutine::FlatResume,
10621091
result: PyResult<crate::frame::ExecutionResult>,
10631092
},
@@ -1855,11 +1884,11 @@ impl VirtualMachine {
18551884

18561885
/// Free a callee frame's data stack storage, if it still owns any.
18571886
#[inline]
1858-
fn release_trampoline_callee(&self, iframe: *mut crate::frame::InterpreterFrame) {
1887+
fn release_trampoline_callee(&self, mut iframe: TrampolineIFrame) {
18591888
// SAFETY: the callee has finished; its storage is the top of this
18601889
// thread's data stack because frames are released in LIFO order.
18611890
unsafe {
1862-
if let Some((base, size)) = (*iframe).release_datastack_frame() {
1891+
if let Some((base, size)) = iframe.as_mut().release_datastack_frame() {
18631892
self.datastack_pop_frame(base, size);
18641893
}
18651894
}
@@ -1888,15 +1917,15 @@ impl VirtualMachine {
18881917
}
18891918
Err(exc) => return GenEntered::Failed(Outcome::Raise(exc)),
18901919
};
1891-
let iframe = state.iframe_ptr();
18921920
// SAFETY: the frame is linked and claimed, so this thread is its
1893-
// only executor for as long as the record below lives.
1894-
let frame = unsafe { &mut *iframe };
1921+
// only executor for as long as the handle below lives.
1922+
let mut iframe = unsafe { TrampolineIFrame::from_ptr(state.iframe_ptr()) };
18951923
if let Some(sent) = sent {
1896-
if let Some((delegate, cont)) = crate::frame::yield_from_delegate(frame, self)
1924+
if let Some((delegate, cont)) =
1925+
crate::frame::yield_from_delegate(iframe.as_mut(), self)
18971926
&& crate::frame::gen_collapse_allowed(self)
18981927
{
1899-
crate::frame::park_at_send(frame, cont);
1928+
crate::frame::park_at_send(iframe.as_mut(), cont);
19001929
self.trampoline_push(SuspendedFrame {
19011930
iframe,
19021931
kind: FrameKind::Gen(state),
@@ -1907,12 +1936,14 @@ impl VirtualMachine {
19071936
value = sent;
19081937
continue;
19091938
}
1910-
frame.localsplus.push_stack(sent);
1939+
iframe.as_mut().localsplus.push_stack(sent);
19111940
}
1941+
let result =
1942+
crate::frame::run_iframe(iframe.as_mut(), crate::frame::Flatten::GenResume, self);
19121943
return GenEntered::Ran {
19131944
iframe,
19141945
state,
1915-
result: crate::frame::run_iframe(frame, crate::frame::Flatten::GenResume, self),
1946+
result,
19161947
};
19171948
}
19181949
}
@@ -1938,17 +1969,19 @@ impl VirtualMachine {
19381969
#[inline(never)]
19391970
fn run_trampoline(
19401971
&self,
1941-
iframe: *mut crate::frame::InterpreterFrame,
1972+
iframe: &mut crate::frame::InterpreterFrame,
19421973
kind: FrameKind,
19431974
start: TrampolineStart,
19441975
) -> PyResult<ExecutionResult> {
19451976
use crate::frame::ExecutionResult;
19461977

1978+
let iframe = TrampolineIFrame::from_mut(iframe);
1979+
19471980
/// What the loop does next. Deliberately small, and holding no frame
19481981
/// state: every ordinary Python-to-Python call passes through here.
19491982
enum Action {
19501983
/// Enter and run the data stack frame a `TailCall` prepared.
1951-
EnterCallee(*mut crate::frame::InterpreterFrame),
1984+
EnterCallee(TrampolineIFrame),
19521985
/// Resume the generator a `GenResume` parked, walking down its
19531986
/// `yield from` chain.
19541987
ResumeGen {
@@ -1975,14 +2008,17 @@ impl VirtualMachine {
19752008
match $result {
19762009
Ok(ExecutionResult::TailCall) => {
19772010
let callee_owner = self.take_pending_tailcall_owner();
1978-
let callee_ptr = self.take_pending_tailcall();
2011+
// SAFETY: the callee was just allocated on this
2012+
// thread's data stack; this is the first handle.
2013+
let callee =
2014+
unsafe { TrampolineIFrame::from_ptr(self.take_pending_tailcall()) };
19792015
self.trampoline_push(SuspendedFrame {
19802016
iframe,
19812017
kind,
19822018
callee_owner: Some(callee_owner),
19832019
cont: crate::frame::GenCont::NONE,
19842020
});
1985-
Action::EnterCallee(callee_ptr)
2021+
Action::EnterCallee(callee)
19862022
}
19872023
Ok(ExecutionResult::GenResume) => {
19882024
let PendingGenResume { jen, value, cont } = self.take_pending_gen_resume();
@@ -2057,21 +2093,18 @@ impl VirtualMachine {
20572093

20582094
loop {
20592095
action = match action {
2060-
Action::EnterCallee(callee_ptr) => {
2061-
// SAFETY: the callee frame was just allocated on this
2062-
// thread's data stack, and nothing has popped it.
2063-
let callee = unsafe { &mut *callee_ptr };
2064-
match self.enter_iframe_unchecked(callee) {
2096+
Action::EnterCallee(mut callee) => {
2097+
match self.enter_iframe_unchecked(callee.as_mut()) {
20652098
Ok(state) => {
20662099
let result = crate::frame::run_iframe(
2067-
callee,
2100+
callee.as_mut(),
20682101
crate::frame::Flatten::CallAndGenResume,
20692102
self,
20702103
);
2071-
dispatch!(callee_ptr, FrameKind::Callee(state), result)
2104+
dispatch!(callee, FrameKind::Callee(state), result)
20722105
}
20732106
Err(exc) => {
2074-
self.release_trampoline_callee(callee_ptr);
2107+
self.release_trampoline_callee(callee);
20752108
Action::Deliver(Outcome::Raise(exc))
20762109
}
20772110
}
@@ -2091,7 +2124,7 @@ impl VirtualMachine {
20912124
// frame it has already suspended, so an outcome always has
20922125
// a caller waiting for it.
20932126
let SuspendedFrame {
2094-
iframe,
2127+
mut iframe,
20952128
kind,
20962129
callee_owner,
20972130
cont,
@@ -2105,16 +2138,13 @@ impl VirtualMachine {
21052138
// than holding it across the caller's next stretch of
21062139
// bytecode.
21072140
drop(callee_owner);
2108-
// SAFETY: a suspended caller's frame stays alive and
2109-
// unmoved until it is resumed here.
2110-
let frame = unsafe { &mut *iframe };
21112141
match outcome {
21122142
// A frame parked mid `yield from` re-yields what its
21132143
// delegate produced, running no instruction of its own.
21142144
Outcome::Value(value)
21152145
if cont.is_some() && crate::frame::gen_collapse_allowed(self) =>
21162146
{
2117-
crate::frame::park_after_yield_from(frame, cont.resumed_at);
2147+
crate::frame::park_after_yield_from(iframe.as_mut(), cont.resumed_at);
21182148
match kind {
21192149
FrameKind::Gen(state) => {
21202150
Action::Deliver(self.trampoline_finish_gen(
@@ -2133,28 +2163,34 @@ impl VirtualMachine {
21332163
}
21342164
}
21352165
Outcome::Value(value) => {
2136-
frame.localsplus.push_stack(value);
2137-
let result = self.trampoline_run(frame, &kind);
2166+
iframe.as_mut().localsplus.push_stack(value);
2167+
let result = self.trampoline_run(iframe.as_mut(), &kind);
21382168
dispatch!(iframe, kind, result)
21392169
}
21402170
Outcome::GenStop(value) => {
21412171
debug_assert!(
21422172
cont.is_some(),
21432173
"a generator finished with no continuation to apply"
21442174
);
2145-
let result =
2146-
match crate::frame::trampoline_gen_stop(frame, value, cont, self) {
2147-
Ok(()) => self.trampoline_run(frame, &kind),
2148-
Err(exc) => Err(exc),
2149-
};
2175+
let result = match crate::frame::trampoline_gen_stop(
2176+
iframe.as_mut(),
2177+
value,
2178+
cont,
2179+
self,
2180+
) {
2181+
Ok(()) => self.trampoline_run(iframe.as_mut(), &kind),
2182+
Err(exc) => Err(exc),
2183+
};
21502184
dispatch!(iframe, kind, result)
21512185
}
21522186
Outcome::Raise(exc) => {
21532187
let result = match crate::frame::trampoline_handle_exception(
2154-
frame, &exc, self,
2188+
iframe.as_mut(),
2189+
&exc,
2190+
self,
21552191
) {
21562192
// Handler found — resume the caller's loop.
2157-
Ok(None) => self.trampoline_run(frame, &kind),
2193+
Ok(None) => self.trampoline_run(iframe.as_mut(), &kind),
21582194
Ok(Some(result)) => Ok(result),
21592195
Err(exc) => Err(exc),
21602196
};

0 commit comments

Comments
 (0)