Skip to content

Commit 2d3da84

Browse files
authored
fix(hermes): exc_counter was being mismanaged in a thread unsafe way. (#545)
* fix(hermes): exc_counter was being mismanaged in a thread unsafe way. This PR fixes that and cleans up redundant parameters in the process as the calls changed subtly to make the fix reliable. * fix(hermes): benchmark build error * fix(hermes): replace drop() with `if let Err(err) =`
1 parent 57e1eef commit 2d3da84

File tree

4 files changed

+61
-84
lines changed

4 files changed

+61
-84
lines changed

hermes/bin/src/app.rs

Lines changed: 6 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@ use std::{collections::HashMap, sync::Arc};
55
use crate::{
66
event::HermesEventPayload,
77
pool,
8-
runtime_context::HermesRuntimeContext,
9-
runtime_extensions::{
10-
init::trait_event::{RteEvent, RteInitEvent},
11-
new_context,
12-
},
138
vfs::Vfs,
149
wasm::module::{Module, ModuleId},
1510
};
@@ -83,20 +78,14 @@ impl Application {
8378
event: &Arc<dyn HermesEventPayload>,
8479
) {
8580
for module in self.indexed_modules.values() {
86-
module_dispatch_event(
87-
module.clone(),
88-
self.name.clone(),
89-
module.id().clone(),
90-
self.vfs.clone(),
91-
event.clone(),
92-
);
81+
module_dispatch_event(module.clone(), self.vfs.clone(), event.clone());
9382
}
9483
}
9584

9685
/// Initialize every module.
9786
pub(crate) fn init(&self) -> anyhow::Result<()> {
9887
for module in self.indexed_modules.values() {
99-
if let Err(e) = module.init(self.name.clone(), Arc::clone(&self.vfs)) {
88+
if let Err(e) = module.init(self.vfs.clone()) {
10089
anyhow::bail!("Failed to initialize module {}: {}", module.id(), e)
10190
}
10291
}
@@ -106,62 +95,28 @@ impl Application {
10695
/// Dispatch event for the target module by the `module_id`.
10796
pub(crate) fn dispatch_event_for_target_module(
10897
&self,
109-
module_id: ModuleId,
98+
module_id: &ModuleId,
11099
event: Arc<dyn HermesEventPayload>,
111100
) -> anyhow::Result<()> {
112101
let module = self
113102
.indexed_modules
114-
.get(&module_id)
103+
.get(module_id)
115104
.ok_or(anyhow::anyhow!("Module {module_id} not found"))?;
116-
module_dispatch_event(
117-
module.clone(),
118-
self.name.clone(),
119-
module_id,
120-
self.vfs.clone(),
121-
event,
122-
);
105+
module_dispatch_event(module.clone(), self.vfs.clone(), event);
123106
Ok(())
124107
}
125108
}
126109

127110
/// Dispatch event
128111
pub(crate) fn module_dispatch_event(
129112
module: Arc<Module>,
130-
app_name: ApplicationName,
131-
module_id: ModuleId,
132113
vfs: Arc<Vfs>,
133114
event: Arc<dyn HermesEventPayload>,
134115
) {
135116
// TODO(@aido-mth): fix how init is processed. https://github.com/input-output-hk/hermes/issues/490
136117
pool::execute(move || {
137-
let runtime_ctx = HermesRuntimeContext::new(
138-
app_name,
139-
module_id,
140-
event.event_name().to_string(),
141-
module.exec_counter(),
142-
vfs,
143-
);
144-
145-
// Advise Runtime Extensions of a new context
146-
// TODO: Better handle errors.
147-
if let Err(err) = RteEvent::new().init(&runtime_ctx) {
148-
tracing::error!("module event initialization failed: {err}");
149-
return;
150-
}
151-
152-
// TODO: (SJ) Remove when all RTE's are migrated.
153-
new_context(&runtime_ctx);
154-
155-
if let Err(err) = module.execute_event(event.as_ref(), runtime_ctx.clone()) {
118+
if let Err(err) = module.execute_event(event.as_ref(), vfs) {
156119
tracing::error!("module event execution failed: {err}");
157-
return;
158120
}
159-
160-
// Advise Runtime Extensions that context can be cleaned up.
161-
drop(
162-
RteEvent::new()
163-
.fini(&runtime_ctx)
164-
.inspect_err(|err| tracing::error!("module event finalization failed: {err}")),
165-
);
166121
});
167122
}

hermes/bin/src/event/queue.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,9 @@ fn targeted_module_event_execution(
100100
},
101101
TargetModule::List(target_modules) => {
102102
for target_module_id in target_modules {
103-
if let Err(err) = app.dispatch_event_for_target_module(
104-
target_module_id.clone(),
105-
event.payload().clone(),
106-
) {
103+
if let Err(err) =
104+
app.dispatch_event_for_target_module(target_module_id, event.payload().clone())
105+
{
107106
tracing::error!("{err}");
108107
}
109108
}

hermes/bin/src/runtime_extensions/hermes/integration_test/event.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,15 @@ pub fn execute_event(
118118
let vfs =
119119
Arc::new(VfsBootstrapper::new(hermes_home_dir.path(), app_name.to_string()).bootstrap()?);
120120

121-
let module_id = module.id().clone();
122121
let result = match event_type {
123122
EventType::Bench => {
124123
let on_bench_event = Arc::new(OnBenchEvent { test, run });
125-
module_dispatch_event(module, app_name, module_id, vfs.clone(), on_bench_event);
124+
module_dispatch_event(module, vfs.clone(), on_bench_event);
126125
BENCH_RESULT_QUEUE.get_or_init(SegQueue::new).pop()
127126
},
128127
EventType::Test => {
129128
let on_test_event = Arc::new(OnTestEvent { test, run });
130-
module_dispatch_event(module, app_name, module_id, vfs.clone(), on_test_event);
129+
module_dispatch_event(module, vfs.clone(), on_test_event);
131130
TEST_RESULT_QUEUE.get_or_init(SegQueue::new).pop()
132131
},
133132
};

hermes/bin/src/wasm/module.rs

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ use crate::{
2828
runtime_extensions::{
2929
bindings::{self, unchecked_exports, LinkOptions},
3030
hermes::init::ComponentInstanceExt as _,
31-
init::trait_module::{RteInitModule, RteModule},
31+
init::{
32+
trait_event::{RteEvent, RteInitEvent},
33+
trait_module::{RteInitModule, RteModule},
34+
},
3235
new_context,
3336
},
3437
vfs::Vfs,
@@ -83,7 +86,13 @@ pub struct Module {
8386
id: ModuleId,
8487

8588
/// Module's execution counter
89+
/// CAN NEVER be used outside this structs methods.
90+
/// NEVER add a getter for it.
91+
/// ONLY ever use `fetch_add` never read its value otherwise.
8692
exc_counter: AtomicU32,
93+
94+
/// The name of the application which owns this module.
95+
app_name: ApplicationName,
8796
}
8897

8998
impl Module {
@@ -123,22 +132,33 @@ impl Module {
123132
engine,
124133
id,
125134
exc_counter: AtomicU32::new(0),
135+
app_name: app_name.clone(),
126136
})
127137
}
128138

139+
/// Make a new context, deliberately private. Do not make public.
140+
fn new_context(
141+
&self,
142+
event_name: &str,
143+
vfs: Arc<Vfs>,
144+
) -> HermesRuntimeContext {
145+
HermesRuntimeContext::new(
146+
self.app_name.clone(),
147+
self.id.clone(),
148+
event_name.to_string(),
149+
// **MUST** be the only place exc_counter is read and updated.
150+
// NEVER read it anywhere else, and never update it anywhere else.
151+
self.exc_counter.fetch_add(1, Ordering::SeqCst),
152+
vfs,
153+
)
154+
}
155+
129156
/// Initializes the WASM module by calling its `init` function.
130157
pub(crate) fn init(
131158
&self,
132-
app_name: ApplicationName,
133159
vfs: Arc<Vfs>,
134160
) -> anyhow::Result<()> {
135-
let runtime_ctx = HermesRuntimeContext::new(
136-
app_name,
137-
self.id.clone(),
138-
"init_function_call".to_string(),
139-
0,
140-
vfs,
141-
);
161+
let runtime_ctx = self.new_context("init_function_call", vfs);
142162

143163
new_context(&runtime_ctx);
144164

@@ -180,15 +200,6 @@ impl Module {
180200
&self.id
181201
}
182202

183-
/// Get the module's execution counter
184-
pub(crate) fn exec_counter(&self) -> u32 {
185-
// Using the highest memory ordering constraint.
186-
// It provides a highest consistency guarantee and in some cases could decrease
187-
// performance.
188-
// We could revise ordering approach for this case in future.
189-
self.exc_counter.load(Ordering::SeqCst)
190-
}
191-
192203
/// Executes a Hermes event by calling some WASM function.
193204
/// This function abstraction over actual execution of the WASM function,
194205
/// actual definition is inside `HermesEventPayload` trait implementation.
@@ -201,9 +212,21 @@ impl Module {
201212
pub(crate) fn execute_event(
202213
&self,
203214
event: &dyn HermesEventPayload,
204-
state: HermesRuntimeContext,
215+
vfs: Arc<Vfs>,
205216
) -> anyhow::Result<()> {
206-
let mut store = WasmStore::new(&self.engine, state);
217+
let runtime_ctx = self.new_context(event.event_name(), vfs);
218+
219+
// Advise Runtime Extensions of a new context
220+
// TODO: Better handle errors.
221+
if let Err(err) = RteEvent::new().init(&runtime_ctx) {
222+
tracing::error!("module event initialization failed: {err}");
223+
return Err(err.into());
224+
}
225+
226+
// TODO: (SJ) Remove when all RTE's are migrated.
227+
new_context(&runtime_ctx);
228+
229+
let mut store = WasmStore::new(&self.engine, runtime_ctx.clone());
207230
let instance = self
208231
.pre_instance
209232
.clone()
@@ -212,11 +235,12 @@ impl Module {
212235

213236
event.execute(&mut ModuleInstance { store, instance })?;
214237

215-
// Using the highest memory ordering constraint.
216-
// It provides a highest consistency guarantee and in some cases could decrease
217-
// performance.
218-
// We could revise ordering approach for this case in future.
219-
self.exc_counter.fetch_add(1, Ordering::SeqCst);
238+
// Advise Runtime Extensions that context can be cleaned up.
239+
if let Err(err) = RteEvent::new().fini(&runtime_ctx) {
240+
//TODO(SJ): Maybe need better error handling...
241+
tracing::error!("module event finalization failed: {err}");
242+
}
243+
220244
Ok(())
221245
}
222246
}
@@ -247,7 +271,7 @@ pub mod bench {
247271
);
248272

249273
b.iter(|| {
250-
module.init(app_name.clone(), vfs.clone()).unwrap();
274+
module.init(vfs.clone()).unwrap();
251275
});
252276
}
253277

0 commit comments

Comments
 (0)