Skip to content

Commit 219802a

Browse files
committed
Fix lasti operations deadlocking
1 parent b8a3a38 commit 219802a

File tree

1 file changed

+23
-15
lines changed

1 file changed

+23
-15
lines changed

vm/src/frame.rs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::fmt;
2+
use std::sync::atomic::{AtomicUsize, Ordering};
23
use std::sync::Mutex;
34

45
use indexmap::IndexMap;
@@ -82,14 +83,14 @@ struct FrameState {
8283
stack: Vec<PyObjectRef>,
8384
/// Block frames, for controlling loops and exceptions
8485
blocks: Vec<Block>,
85-
/// index of last instruction ran
86-
lasti: usize,
8786
}
8887

8988
#[pyclass]
9089
pub struct Frame {
9190
pub code: PyCodeRef,
9291
pub scope: Scope,
92+
/// index of last instruction ran
93+
pub lasti: AtomicUsize,
9394
state: Mutex<FrameState>,
9495
}
9596

@@ -160,10 +161,10 @@ impl Frame {
160161
Frame {
161162
code,
162163
scope,
164+
lasti: AtomicUsize::new(0),
163165
state: Mutex::new(FrameState {
164166
stack: Vec::new(),
165167
blocks: Vec::new(),
166-
lasti: 0,
167168
}),
168169
}
169170
}
@@ -175,6 +176,7 @@ impl FrameRef {
175176
let exec = ExecutingFrame {
176177
code: &self.code,
177178
scope: &self.scope,
179+
lasti: &self.lasti,
178180
object: &self,
179181
state: &mut state,
180182
};
@@ -208,15 +210,15 @@ impl FrameRef {
208210
}
209211

210212
pub fn current_location(&self) -> bytecode::Location {
211-
self.with_exec(|exec| exec.current_location())
213+
self.code.locations[self.lasti.load(Ordering::Relaxed)]
212214
}
213215

214216
pub fn yield_from_target(&self) -> Option<PyObjectRef> {
215217
self.with_exec(|exec| exec.yield_from_target())
216218
}
217219

218220
pub fn lasti(&self) -> usize {
219-
self.state.lock().unwrap().lasti
221+
self.lasti.load(Ordering::Relaxed)
220222
}
221223
}
222224

@@ -226,6 +228,7 @@ struct ExecutingFrame<'a> {
226228
code: &'a PyCodeRef,
227229
scope: &'a Scope,
228230
object: &'a FrameRef,
231+
lasti: &'a AtomicUsize,
229232
state: &'a mut FrameState,
230233
}
231234

@@ -250,7 +253,7 @@ impl ExecutingFrame<'_> {
250253
let next = exception.traceback();
251254

252255
let new_traceback =
253-
PyTraceback::new(next, self.object.clone(), self.state.lasti, loc.row());
256+
PyTraceback::new(next, self.object.clone(), self.lasti(), loc.row());
254257
exception.set_traceback(Some(new_traceback.into_ref(vm)));
255258
vm_trace!("Adding to traceback: {:?} {:?}", new_traceback, lineno);
256259

@@ -271,8 +274,7 @@ impl ExecutingFrame<'_> {
271274
}
272275

273276
fn yield_from_target(&self) -> Option<PyObjectRef> {
274-
if let Some(bytecode::Instruction::YieldFrom) = self.code.instructions.get(self.state.lasti)
275-
{
277+
if let Some(bytecode::Instruction::YieldFrom) = self.code.instructions.get(self.lasti()) {
276278
Some(self.last_value())
277279
} else {
278280
None
@@ -293,7 +295,7 @@ impl ExecutingFrame<'_> {
293295
};
294296
res.or_else(|err| {
295297
self.pop_value();
296-
self.state.lasti += 1;
298+
self.lasti.fetch_add(1, Ordering::Relaxed);
297299
let val = objiter::stop_iter_value(vm, &err)?;
298300
self._send(coro, val, vm)
299301
})
@@ -312,8 +314,7 @@ impl ExecutingFrame<'_> {
312314
fn execute_instruction(&mut self, vm: &VirtualMachine) -> FrameResult {
313315
vm.check_signals()?;
314316

315-
let instruction = &self.code.instructions[self.state.lasti];
316-
self.state.lasti += 1;
317+
let instruction = &self.code.instructions[self.lasti.fetch_add(1, Ordering::Relaxed)];
317318

318319
flame_guard!(format!("Frame::execute_instruction({:?})", instruction));
319320

@@ -1116,7 +1117,7 @@ impl ExecutingFrame<'_> {
11161117
match result {
11171118
ExecutionResult::Yield(value) => {
11181119
// Set back program counter:
1119-
self.state.lasti -= 1;
1120+
self.lasti.fetch_sub(1, Ordering::Relaxed);
11201121
Ok(Some(ExecutionResult::Yield(value)))
11211122
}
11221123
ExecutionResult::Return(value) => {
@@ -1166,8 +1167,8 @@ impl ExecutingFrame<'_> {
11661167
fn jump(&mut self, label: bytecode::Label) {
11671168
let target_pc = self.code.label_map[&label];
11681169
#[cfg(feature = "vm-tracing-logging")]
1169-
trace!("jump from {:?} to {:?}", self.state.lasti, target_pc);
1170-
self.state.lasti = target_pc;
1170+
trace!("jump from {:?} to {:?}", self.lasti(), target_pc);
1171+
self.lasti.store(target_pc, Ordering::Relaxed);
11711172
}
11721173

11731174
/// The top of stack contains the iterator, lets push it forward
@@ -1418,8 +1419,15 @@ impl ExecutingFrame<'_> {
14181419
Ok(None)
14191420
}
14201421

1422+
fn lasti(&self) -> usize {
1423+
// it's okay to make this Relaxed, because we know that we only
1424+
// mutate lasti if the mutex is held, and any other thread that
1425+
// wants to guarantee the value of this will use a Lock anyway
1426+
self.lasti.load(Ordering::Relaxed)
1427+
}
1428+
14211429
fn current_location(&self) -> bytecode::Location {
1422-
self.code.locations[self.state.lasti]
1430+
self.code.locations[self.lasti()]
14231431
}
14241432

14251433
fn push_block(&mut self, typ: BlockType) {

0 commit comments

Comments
 (0)