Skip to content

Commit f9cefbd

Browse files
committed
runtime: Use interior mutability in AscHeapCtx
1 parent 456fc3a commit f9cefbd

File tree

2 files changed

+60
-39
lines changed

2 files changed

+60
-39
lines changed

runtime/wasm/src/module/context.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,8 @@ impl WasmInstanceContext<'_> {
4848
self.inner.data_mut()
4949
}
5050

51-
pub fn asc_heap_ref(&self) -> &AscHeapCtx {
52-
self.as_ref().asc_heap_ref()
53-
}
54-
55-
pub fn asc_heap_mut(&mut self) -> &mut AscHeapCtx {
56-
self.as_mut().asc_heap_mut()
51+
pub fn asc_heap(&self) -> &Arc<AscHeapCtx> {
52+
self.as_ref().asc_heap()
5753
}
5854

5955
pub fn suspend_timeout(&mut self) {
@@ -96,7 +92,7 @@ pub struct WasmInstanceData {
9692

9793
// This option is needed to break the cyclic dependency between, instance, store, and context.
9894
// during execution it should always be populated.
99-
asc_heap: Option<AscHeapCtx>,
95+
asc_heap: Option<Arc<AscHeapCtx>>,
10096
}
10197

10298
impl WasmInstanceData {
@@ -117,15 +113,12 @@ impl WasmInstanceData {
117113
}
118114
}
119115

120-
pub fn set_asc_heap(&mut self, asc_heap: AscHeapCtx) {
116+
pub fn set_asc_heap(&mut self, asc_heap: Arc<AscHeapCtx>) {
121117
self.asc_heap = Some(asc_heap);
122118
}
123119

124-
pub fn asc_heap_ref(&self) -> &AscHeapCtx {
125-
self.asc_heap.as_ref().unwrap()
126-
}
127-
pub fn asc_heap_mut(&mut self) -> &mut AscHeapCtx {
128-
self.asc_heap.as_mut().unwrap()
120+
pub fn asc_heap(&self) -> &Arc<AscHeapCtx> {
121+
self.asc_heap.as_ref().expect("asc_heap not set")
129122
}
130123

131124
pub fn take_state(mut self) -> BlockState {

runtime/wasm/src/module/mod.rs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::convert::TryFrom;
22
use std::mem::MaybeUninit;
3+
use std::sync::Mutex;
34

45
use anyhow::anyhow;
56
use anyhow::Error;
@@ -134,6 +135,19 @@ fn is_trap_deterministic(trap: &Error) -> bool {
134135
}
135136
}
136137

138+
struct Arena {
139+
// First free byte in the current arena. Set on the first call to `raw_new`.
140+
start: i32,
141+
// Number of free bytes starting from `arena_start_ptr`.
142+
size: i32,
143+
}
144+
145+
impl Arena {
146+
fn new() -> Self {
147+
Self { start: 0, size: 0 }
148+
}
149+
}
150+
137151
#[derive(Copy, Clone)]
138152
pub struct ExperimentalFeatures {
139153
pub allow_non_deterministic_ipfs: bool,
@@ -154,19 +168,15 @@ pub struct AscHeapCtx {
154168
// is zeroed when initialized or grown.
155169
memory: Memory,
156170

157-
// First free byte in the current arena. Set on the first call to `raw_new`.
158-
arena_start_ptr: i32,
159-
160-
// Number of free bytes starting from `arena_start_ptr`.
161-
arena_free_size: i32,
171+
arena: Mutex<Arena>,
162172
}
163173

164174
impl AscHeapCtx {
165175
pub(crate) fn new(
166176
instance: &wasmtime::Instance,
167177
ctx: &mut WasmInstanceContext<'_>,
168178
api_version: Version,
169-
) -> anyhow::Result<AscHeapCtx> {
179+
) -> anyhow::Result<Arc<AscHeapCtx>> {
170180
// Provide access to the WASM runtime linear memory
171181
let memory = instance
172182
.get_memory(ctx.as_context_mut(), "memory")
@@ -194,14 +204,33 @@ impl AscHeapCtx {
194204
),
195205
};
196206

197-
Ok(AscHeapCtx {
207+
Ok(Arc::new(AscHeapCtx {
198208
memory_allocate,
199209
memory,
200-
arena_start_ptr: 0,
201-
arena_free_size: 0,
210+
arena: Mutex::new(Arena::new()),
202211
api_version,
203212
id_of_type,
204-
})
213+
}))
214+
}
215+
216+
fn arena_start_ptr(&self) -> i32 {
217+
self.arena.lock().unwrap().start
218+
}
219+
220+
fn arena_free_size(&self) -> i32 {
221+
self.arena.lock().unwrap().size
222+
}
223+
224+
fn set_arena(&self, start_ptr: i32, size: i32) {
225+
let mut arena = self.arena.lock().unwrap();
226+
arena.start = start_ptr;
227+
arena.size = size;
228+
}
229+
230+
fn allocated(&self, size: i32) {
231+
let mut arena = self.arena.lock().unwrap();
232+
arena.start += size;
233+
arena.size -= size;
205234
}
206235
}
207236

@@ -229,21 +258,20 @@ impl AscHeap for WasmInstanceContext<'_> {
229258
static MIN_ARENA_SIZE: i32 = 10_000;
230259

231260
let size = i32::try_from(bytes.len()).unwrap();
232-
if size > self.asc_heap_ref().arena_free_size {
261+
if size > self.asc_heap().arena_free_size() {
233262
// Allocate a new arena. Any free space left in the previous arena is left unused. This
234263
// causes at most half of memory to be wasted, which is acceptable.
235-
let arena_size = size.max(MIN_ARENA_SIZE);
264+
let mut arena_size = size.max(MIN_ARENA_SIZE);
236265

237266
// Unwrap: This may panic if more memory needs to be requested from the OS and that
238267
// fails. This error is not deterministic since it depends on the operating conditions
239268
// of the node.
240-
let memory_allocate = self.asc_heap_ref().memory_allocate.clone();
241-
self.asc_heap_mut().arena_start_ptr = memory_allocate
269+
let memory_allocate = &self.asc_heap().cheap_clone().memory_allocate;
270+
let mut start_ptr = memory_allocate
242271
.call(self.as_context_mut(), arena_size)
243272
.unwrap();
244-
self.asc_heap_mut().arena_free_size = arena_size;
245273

246-
match &self.asc_heap_ref().api_version {
274+
match &self.asc_heap().api_version {
247275
version if *version <= Version::new(0, 0, 4) => {}
248276
_ => {
249277
// This arithmetic is done because when you call AssemblyScripts's `__alloc`
@@ -252,27 +280,27 @@ impl AscHeap for WasmInstanceContext<'_> {
252280
// `mmInfo` has size of 4, and everything allocated on AssemblyScript memory
253281
// should have alignment of 16, this means we need to do a 12 offset on these
254282
// big chunks of untyped allocation.
255-
self.asc_heap_mut().arena_start_ptr += 12;
256-
self.asc_heap_mut().arena_free_size -= 12;
283+
start_ptr += 12;
284+
arena_size -= 12;
257285
}
258286
};
287+
self.asc_heap().set_arena(start_ptr, arena_size);
259288
};
260289

261-
let ptr = self.asc_heap_ref().arena_start_ptr as usize;
290+
let ptr = self.asc_heap().arena_start_ptr() as usize;
262291

263292
// Unwrap: We have just allocated enough space for `bytes`.
264-
let memory = self.asc_heap_ref().memory;
293+
let memory = self.asc_heap().memory;
265294
memory.write(self.as_context_mut(), ptr, bytes).unwrap();
266-
self.asc_heap_mut().arena_start_ptr += size;
267-
self.asc_heap_mut().arena_free_size -= size;
295+
self.asc_heap().allocated(size);
268296

269297
Ok(ptr as u32)
270298
}
271299

272300
fn read_u32(&self, offset: u32, gas: &GasCounter) -> Result<u32, DeterministicHostError> {
273301
gas.consume_host_fn_with_metrics(Gas::new(GAS_COST_LOAD as u64 * 4), "read_u32")?;
274302
let mut bytes = [0; 4];
275-
self.asc_heap_ref()
303+
self.asc_heap()
276304
.memory
277305
.read(self, offset as usize, &mut bytes)
278306
.map_err(|_| {
@@ -302,7 +330,7 @@ impl AscHeap for WasmInstanceContext<'_> {
302330

303331
// TODO: Do we still need this? Can we use read directly?
304332
let src = self
305-
.asc_heap_ref()
333+
.asc_heap()
306334
.memory
307335
.data(self)
308336
.get(offset..)
@@ -317,11 +345,11 @@ impl AscHeap for WasmInstanceContext<'_> {
317345
}
318346

319347
fn api_version(&self) -> Version {
320-
self.asc_heap_ref().api_version.clone()
348+
self.asc_heap().api_version.clone()
321349
}
322350

323351
fn asc_type_id(&mut self, type_id_index: IndexForAscTypeId) -> Result<u32, HostExportError> {
324-
let func = self.asc_heap_ref().id_of_type.clone().unwrap();
352+
let func = self.asc_heap().id_of_type.clone().unwrap();
325353

326354
// Unwrap ok because it's only called on correct apiVersion, look for AscPtr::generate_header
327355
func.call(self.as_context_mut(), type_id_index as u32)

0 commit comments

Comments
 (0)