Skip to content

Commit 37bb428

Browse files
authored
fix: avoid take and put of state (#1328)
Addresses a potential deadlock
1 parent a90d5f7 commit 37bb428

2 files changed

Lines changed: 32 additions & 24 deletions

File tree

fendermint/app/src/app.rs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -324,18 +324,19 @@ where
324324
guard.take().expect("exec state empty")
325325
}
326326

327-
/// Take the execution state, update it, put it back, return the output.
328-
async fn modify_exec_state<T, F, R>(&self, f: F) -> Result<T>
327+
/// Update the execution state using the provided closure.
328+
///
329+
/// Note: Deals with panics in the user provided closure as well.
330+
async fn modify_exec_state<T, G, F>(&self, generator: G) -> Result<T>
329331
where
330-
F: FnOnce(FvmExecState<BS>) -> R,
331-
R: Future<Output = Result<(FvmExecState<BS>, T)>>,
332+
G: for<'s> FnOnce(&'s mut FvmExecState<BS>) -> F,
333+
F: Future<Output = Result<T>>,
334+
T: 'static,
332335
{
333336
let mut guard = self.exec_state.lock().await;
334-
let state = guard.take().expect("exec state empty");
335-
336-
let (state, ret) = f(state).await?;
337-
338-
*guard = Some(state);
337+
let maybe_state = guard.as_mut();
338+
let state = maybe_state.expect("exec state empty");
339+
let ret = generator(state).await?;
339340

340341
Ok(ret)
341342
}
@@ -418,10 +419,18 @@ where
418419
async fn refresh_validators_cache(&self) -> Result<()> {
419420
// TODO: This should be read only state, but we can't use the read-only view here
420421
// because it hasn't been committed to state store yet.
421-
self.modify_exec_state(|mut s| async {
422-
let mut cache = self.validators_cache.lock().await;
423-
*cache = Some(ValidatorCache::new_from_state(&mut s)?);
424-
Ok((s, ()))
422+
let mut cache = self.validators_cache.lock().await;
423+
self.modify_exec_state(|s| {
424+
// we need to leave this outside the closure
425+
// otherwise `s`' lifetime would be captured by the
426+
// closure causing unresolvable liftetime conflicts
427+
// this is fine since we execute the future directly
428+
// after calling the generator closure.
429+
let x = ValidatorCache::new_from_state(s);
430+
async {
431+
*cache = Some(x?);
432+
Ok(())
433+
}
425434
})
426435
.await?;
427436

fendermint/vm/interpreter/src/fvm/interpreter.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct Actor {
4343
state: ActorState,
4444
}
4545

46+
/// Interprets messages as received from the ABCI layer
4647
#[derive(Clone)]
4748
pub struct FvmMessagesInterpreter<DB, C>
4849
where
@@ -107,19 +108,17 @@ where
107108
state: &FvmExecState<ReadOnlyBlockstore<DB>>,
108109
msg: &FvmMessage,
109110
) -> Result<CheckResponse> {
110-
let Actor {
111+
let Some(Actor {
111112
id: _,
112113
state: actor,
113-
} = match self.lookup_actor(state, &msg.from)? {
114-
Some(actor) => actor,
115-
None => {
116-
return Ok(CheckResponse::new(
117-
msg,
118-
ExitCode::SYS_SENDER_INVALID,
119-
None,
120-
None,
121-
))
122-
}
114+
}) = self.lookup_actor(state, &msg.from)?
115+
else {
116+
return Ok(CheckResponse::new(
117+
msg,
118+
ExitCode::SYS_SENDER_INVALID,
119+
None,
120+
None,
121+
));
123122
};
124123

125124
let balance_needed = msg.gas_fee_cap.clone() * msg.gas_limit;

0 commit comments

Comments
 (0)