Skip to content

Commit 5b02079

Browse files
committed
runtime: Refactor and rename the instance handle and context
1 parent 66b1712 commit 5b02079

File tree

4 files changed

+104
-112
lines changed

4 files changed

+104
-112
lines changed

runtime/wasm/src/host_exports.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use web3::types::H160;
1818

1919
use graph_graphql::prelude::validate_entity;
2020

21-
use crate::module::WasmInstance;
21+
use crate::module::{WasmInstance, WasmInstanceContext};
2222

2323
pub(crate) struct HostExports {
2424
subgraph_id: SubgraphDeploymentId,
@@ -351,7 +351,7 @@ impl HostExports {
351351
// parameter is passed to the callback without any changes
352352
pub(crate) fn ipfs_map(
353353
link_resolver: &Arc<dyn LinkResolver>,
354-
module: &mut WasmInstance,
354+
module: &mut WasmInstanceContext,
355355
link: String,
356356
callback: &str,
357357
user_data: store::Value,

runtime/wasm/src/module/mod.rs

Lines changed: 84 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -35,43 +35,56 @@ mod test;
3535
const TRAP_TIMEOUT: &str = "trap: interrupt";
3636

3737
/// Handle to a WASM instance, which is terminated if and only if this is dropped.
38-
pub(crate) struct WasmInstanceHandle {
39-
// This is the only reference to `WasmInstance` that's not within the instance itself, so we can
40-
// always borrow the `RefCell` with no concern for race conditions.
38+
pub(crate) struct WasmInstance {
39+
instance: wasmtime::Instance,
40+
41+
// This is the only reference to `WasmInstanceContext` that's not within the instance itself, so
42+
// we can always borrow the `RefCell` with no concern for race conditions.
4143
//
4244
// Also this is the only strong reference, so the instance will be dropped once this is dropped.
4345
// The weak references are circulary held by instance itself through host exports.
44-
instance: Rc<RefCell<Option<WasmInstance>>>,
46+
instance_ctx: Rc<RefCell<Option<WasmInstanceContext>>>,
4547
}
4648

47-
impl Drop for WasmInstanceHandle {
49+
impl Drop for WasmInstance {
4850
fn drop(&mut self) {
4951
// Assert that the instance will be dropped.
50-
assert_eq!(Rc::strong_count(&self.instance), 1);
52+
assert_eq!(Rc::strong_count(&self.instance_ctx), 1);
5153
}
5254
}
5355

54-
impl WasmInstanceHandle {
56+
/// Proxies to the WasmInstanceContext.
57+
impl AscHeap for WasmInstance {
58+
fn raw_new(&mut self, bytes: &[u8]) -> u32 {
59+
let mut ctx = RefMut::map(self.instance_ctx.borrow_mut(), |i| i.as_mut().unwrap());
60+
ctx.raw_new(bytes)
61+
}
62+
63+
fn get(&self, offset: u32, size: u32) -> Vec<u8> {
64+
self.instance_ctx().get(offset, size)
65+
}
66+
}
67+
68+
impl WasmInstance {
5569
pub(crate) fn handle_json_callback(
5670
mut self,
5771
handler_name: &str,
5872
value: &serde_json::Value,
5973
user_data: &store::Value,
6074
) -> Result<BlockState, anyhow::Error> {
61-
let value = self.instance_mut().asc_new(value);
62-
let user_data = self.instance_mut().asc_new(user_data);
75+
let value = self.asc_new(value);
76+
let user_data = self.asc_new(user_data);
6377

6478
// Invoke the callback
6579
let func = self
66-
.instance()
6780
.instance
6881
.get_func(handler_name)
6982
.with_context(|| format!("function {} not found", handler_name))?
7083
.get2()?;
7184
func(value.wasm_ptr(), user_data.wasm_ptr())
7285
.with_context(|| format!("Failed to handle callback '{}'", handler_name))?;
7386

74-
Ok(self.take_instance().ctx.state)
87+
Ok(self.take_ctx().ctx.state)
7588
}
7689

7790
pub(crate) fn handle_ethereum_log(
@@ -81,42 +94,40 @@ impl WasmInstanceHandle {
8194
log: Arc<Log>,
8295
params: Vec<LogParam>,
8396
) -> Result<BlockState, anyhow::Error> {
84-
let block = self.instance().ctx.block.clone();
97+
let block = self.instance_ctx().ctx.block.clone();
8598

8699
// Prepare an EthereumEvent for the WASM runtime
87100
// Decide on the destination type using the mapping
88101
// api version provided in the subgraph manifest
89-
let event = if self.instance().ctx.host_exports.api_version >= Version::new(0, 0, 2) {
90-
self.instance_mut()
91-
.asc_new::<AscEthereumEvent<AscEthereumTransaction_0_0_2>, _>(&EthereumEventData {
92-
block: EthereumBlockData::from(block.as_ref()),
93-
transaction: EthereumTransactionData::from(transaction.deref()),
94-
address: log.address,
95-
log_index: log.log_index.unwrap_or(U256::zero()),
96-
transaction_log_index: log.log_index.unwrap_or(U256::zero()),
97-
log_type: log.log_type.clone(),
98-
params,
99-
})
100-
.erase()
102+
let event = if self.instance_ctx().ctx.host_exports.api_version >= Version::new(0, 0, 2) {
103+
self.asc_new::<AscEthereumEvent<AscEthereumTransaction_0_0_2>, _>(&EthereumEventData {
104+
block: EthereumBlockData::from(block.as_ref()),
105+
transaction: EthereumTransactionData::from(transaction.deref()),
106+
address: log.address,
107+
log_index: log.log_index.unwrap_or(U256::zero()),
108+
transaction_log_index: log.log_index.unwrap_or(U256::zero()),
109+
log_type: log.log_type.clone(),
110+
params,
111+
})
112+
.erase()
101113
} else {
102-
self.instance_mut()
103-
.asc_new::<AscEthereumEvent<AscEthereumTransaction>, _>(&EthereumEventData {
104-
block: EthereumBlockData::from(block.as_ref()),
105-
transaction: EthereumTransactionData::from(transaction.deref()),
106-
address: log.address,
107-
log_index: log.log_index.unwrap_or(U256::zero()),
108-
transaction_log_index: log.log_index.unwrap_or(U256::zero()),
109-
log_type: log.log_type.clone(),
110-
params,
111-
})
112-
.erase()
114+
self.asc_new::<AscEthereumEvent<AscEthereumTransaction>, _>(&EthereumEventData {
115+
block: EthereumBlockData::from(block.as_ref()),
116+
transaction: EthereumTransactionData::from(transaction.deref()),
117+
address: log.address,
118+
log_index: log.log_index.unwrap_or(U256::zero()),
119+
transaction_log_index: log.log_index.unwrap_or(U256::zero()),
120+
log_type: log.log_type.clone(),
121+
params,
122+
})
123+
.erase()
113124
};
114125

115126
// Invoke the event handler
116127
self.invoke_handler(handler_name, event)?;
117128

118129
// Return the output state
119-
Ok(self.take_instance().ctx.state)
130+
Ok(self.take_ctx().ctx.state)
120131
}
121132

122133
pub(crate) fn handle_ethereum_call(
@@ -130,55 +141,56 @@ impl WasmInstanceHandle {
130141
let call = EthereumCallData {
131142
to: call.to,
132143
from: call.from,
133-
block: EthereumBlockData::from(self.instance().ctx.block.as_ref()),
144+
block: EthereumBlockData::from(self.instance_ctx().ctx.block.as_ref()),
134145
transaction: EthereumTransactionData::from(transaction.deref()),
135146
inputs,
136147
outputs,
137148
};
138-
let arg = if self.instance().ctx.host_exports.api_version >= Version::new(0, 0, 3) {
139-
self.instance_mut()
140-
.asc_new::<AscEthereumCall_0_0_3, _>(&call)
141-
.erase()
149+
let arg = if self.instance_ctx().ctx.host_exports.api_version >= Version::new(0, 0, 3) {
150+
self.asc_new::<AscEthereumCall_0_0_3, _>(&call).erase()
142151
} else {
143-
self.instance_mut()
144-
.asc_new::<AscEthereumCall, _>(&call)
145-
.erase()
152+
self.asc_new::<AscEthereumCall, _>(&call).erase()
146153
};
147154

148155
self.invoke_handler(handler_name, arg)?;
149156

150-
Ok(self.take_instance().ctx.state)
157+
Ok(self.take_ctx().ctx.state)
151158
}
152159

153160
pub(crate) fn handle_ethereum_block(
154161
mut self,
155162
handler_name: &str,
156163
) -> Result<BlockState, anyhow::Error> {
157-
let block = EthereumBlockData::from(self.instance().ctx.block.as_ref());
164+
let block = EthereumBlockData::from(self.instance_ctx().ctx.block.as_ref());
158165

159166
// Prepare an EthereumBlock for the WASM runtime
160-
let arg = self.instance_mut().asc_new(&block);
167+
let arg = self.asc_new(&block);
161168

162169
self.invoke_handler(handler_name, arg)?;
163170

164-
Ok(self.take_instance().ctx.state)
171+
Ok(self.take_ctx().ctx.state)
165172
}
166173

167-
pub(crate) fn instance_mut(&mut self) -> RefMut<'_, WasmInstance> {
168-
RefMut::map(self.instance.borrow_mut(), |i| i.as_mut().unwrap())
174+
pub(crate) fn take_ctx(&mut self) -> WasmInstanceContext {
175+
self.instance_ctx.borrow_mut().take().unwrap()
169176
}
170177

171-
pub(crate) fn take_instance(&mut self) -> WasmInstance {
172-
self.instance.borrow_mut().take().unwrap()
178+
pub(crate) fn instance_ctx(&self) -> std::cell::Ref<'_, WasmInstanceContext> {
179+
std::cell::Ref::map(self.instance_ctx.borrow(), |i| i.as_ref().unwrap())
173180
}
174181

175-
pub(crate) fn instance(&self) -> std::cell::Ref<'_, WasmInstance> {
176-
std::cell::Ref::map(self.instance.borrow(), |i| i.as_ref().unwrap())
182+
#[cfg(test)]
183+
pub(crate) fn instance_ctx_mut(&self) -> std::cell::RefMut<'_, WasmInstanceContext> {
184+
std::cell::RefMut::map(self.instance_ctx.borrow_mut(), |i| i.as_mut().unwrap())
185+
}
186+
187+
#[cfg(test)]
188+
pub(crate) fn get_func(&self, func_name: &str) -> wasmtime::Func {
189+
self.instance.get_func(func_name).unwrap()
177190
}
178191

179192
fn invoke_handler<C>(&mut self, handler: &str, arg: AscPtr<C>) -> Result<(), anyhow::Error> {
180193
let func = self
181-
.instance()
182194
.instance
183195
.get_func(handler)
184196
.with_context(|| format!("function {} not found", handler))?;
@@ -190,7 +202,7 @@ impl WasmInstanceHandle {
190202
format!(
191203
"Handler '{}' hit the timeout of '{}' seconds",
192204
handler,
193-
self.instance().timeout.unwrap().as_secs()
205+
self.instance_ctx().timeout.unwrap().as_secs()
194206
),
195207
)
196208
} else {
@@ -204,11 +216,9 @@ impl WasmInstanceHandle {
204216
///
205217
/// ```compile_fail
206218
/// fn assert_sync<T: Sync>() {}
207-
/// assert_sync::<WasmInstance>();
219+
/// assert_sync::<WasmInstanceContext>();
208220
/// ```
209-
pub(crate) struct WasmInstance {
210-
instance: wasmtime::Instance,
211-
221+
pub(crate) struct WasmInstanceContext {
212222
// In the future there may be multiple memories, but currently there is only one memory per
213223
// module. And at least AS calls it "memory". There is no uninitialized memory in Wasm, memory
214224
// is zeroed when initialized or grown.
@@ -237,12 +247,12 @@ impl WasmInstance {
237247
ctx: MappingContext,
238248
host_metrics: Arc<HostMetrics>,
239249
timeout: Option<Duration>,
240-
) -> Result<WasmInstanceHandle, anyhow::Error> {
250+
) -> Result<WasmInstance, anyhow::Error> {
241251
let mut linker = wasmtime::Linker::new(&wasmtime::Store::new(valid_module.module.engine()));
242252

243253
// Used by exports to access the instance context. It is `None` while the module is not yet
244254
// instantiated. A desirable consequence is that start function cannot access host exports.
245-
let shared_instance: Rc<RefCell<Option<WasmInstance>>> = Rc::new(RefCell::new(None));
255+
let shared_ctx: Rc<RefCell<Option<WasmInstanceContext>>> = Rc::new(RefCell::new(None));
246256

247257
macro_rules! link {
248258
($wasm_name:expr, $rust_name:ident, $($param:ident),*) => {
@@ -258,12 +268,12 @@ impl WasmInstance {
258268

259269
// link an import with all the modules that require it.
260270
for module in modules {
261-
let func_shared_instance = Rc::downgrade(&shared_instance);
271+
let func_shared_ctx = Rc::downgrade(&shared_ctx);
262272
linker.func(
263273
module,
264274
$wasm_name,
265275
move |$($param: u32),*| {
266-
let instance = func_shared_instance.upgrade().unwrap();
276+
let instance = func_shared_ctx.upgrade().unwrap();
267277
let mut instance = instance.borrow_mut();
268278
let instance = instance.as_mut().unwrap();
269279
let _section = instance.host_metrics.stopwatch.start_section($section);
@@ -283,10 +293,10 @@ impl WasmInstance {
283293
.flatten();
284294

285295
for module in modules {
286-
let func_shared_instance = Rc::downgrade(&shared_instance);
296+
let func_shared_ctx = Rc::downgrade(&shared_ctx);
287297
linker.func(module, "store.get", move |entity_ptr: u32, id_ptr: u32| {
288298
let start = Instant::now();
289-
let instance = func_shared_instance.upgrade().unwrap();
299+
let instance = func_shared_ctx.upgrade().unwrap();
290300
let mut instance = instance.borrow_mut();
291301
let instance = instance.as_mut().unwrap();
292302
let stopwatch = &instance.host_metrics.stopwatch;
@@ -308,10 +318,10 @@ impl WasmInstance {
308318
.flatten();
309319

310320
for module in modules {
311-
let func_shared_instance = Rc::downgrade(&shared_instance);
321+
let func_shared_ctx = Rc::downgrade(&shared_ctx);
312322
linker.func(module, "ethereum.call", move |call_ptr: u32| {
313323
let start = Instant::now();
314-
let instance = func_shared_instance.upgrade().unwrap();
324+
let instance = func_shared_ctx.upgrade().unwrap();
315325
let mut instance = instance.borrow_mut();
316326
let instance = instance.as_mut().unwrap();
317327
let stopwatch = &instance.host_metrics.stopwatch;
@@ -441,8 +451,7 @@ impl WasmInstance {
441451
});
442452
}
443453

444-
*shared_instance.borrow_mut() = Some(WasmInstance {
445-
instance,
454+
*shared_ctx.borrow_mut() = Some(WasmInstanceContext {
446455
memory_allocate: Box::new(memory_allocate),
447456
memory,
448457
ctx,
@@ -456,13 +465,14 @@ impl WasmInstance {
456465
arena_start_ptr: 0,
457466
});
458467

459-
Ok(WasmInstanceHandle {
460-
instance: shared_instance.clone(),
468+
Ok(WasmInstance {
469+
instance: instance,
470+
instance_ctx: shared_ctx,
461471
})
462472
}
463473
}
464474

465-
impl AscHeap for WasmInstance {
475+
impl AscHeap for WasmInstanceContext {
466476
fn raw_new(&mut self, bytes: &[u8]) -> u32 {
467477
// We request large chunks from the AssemblyScript allocator to use as arenas that we
468478
// manage directly.
@@ -511,7 +521,7 @@ impl AscHeap for WasmInstance {
511521
}
512522

513523
// Implementation of externals.
514-
impl WasmInstance {
524+
impl WasmInstanceContext {
515525
/// function abort(message?: string | null, fileName?: string | null, lineNumber?: u32, columnNumber?: u32): void
516526
/// Always returns a trap.
517527
fn abort(

0 commit comments

Comments
 (0)