Skip to content

Commit 5901cce

Browse files
committed
Change sandbox restore to be a bit more judicious about when to restore
When we are loading from a snapshot, we can get away with just a restore and never going through a wasmtime load. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com>
1 parent d1642dc commit 5901cce

2 files changed

Lines changed: 82 additions & 6 deletions

File tree

src/hyperlight_wasm/src/sandbox/loaded_wasm_sandbox.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,14 @@ impl LoadedWasmSandbox {
112112
}
113113
}
114114

115-
/// Unload the wasm module and return a `WasmSandbox` that can be used to load another module.
115+
/// Unload the wasm module and return a `WasmSandbox` that can be
116+
/// used to load another module.
116117
///
117-
/// This method internally calls [`restore()`](Self::restore) to reset the sandbox to its
118-
/// pre-module state, which also clears any poisoned state. This means `unload_module()`
119-
/// can be called on a poisoned sandbox to recover it.
118+
/// This method defers calling [`restore()`](Self::restore) to
119+
/// reset the sandbox to its pre-module state until a new module
120+
/// is loaded. However, the sandbox will always be restored when a
121+
/// new module is loaded, so a poisoned sandbox can be recovered
122+
/// by unloading and reloading a module.
120123
pub fn unload_module(mut self) -> Result<WasmSandbox> {
121124
let sandbox = self
122125
.inner

src/hyperlight_wasm/src/sandbox/wasm_sandbox.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub struct WasmSandbox {
4141
// Snapshot of state of an initial WasmSandbox (runtime loaded, but no guest module code loaded).
4242
// Used for LoadedWasmSandbox to be able restore state back to WasmSandbox
4343
snapshot: Option<Arc<Snapshot>>,
44+
needs_restore: bool,
4445
}
4546

4647
const MAPPED_BINARY_VA: u64 = 0x1_0000_0000u64;
@@ -56,6 +57,7 @@ impl WasmSandbox {
5657
Ok(WasmSandbox {
5758
inner: Some(inner),
5859
snapshot: Some(snapshot),
60+
needs_restore: false,
5961
})
6062
}
6163

@@ -64,24 +66,41 @@ impl WasmSandbox {
6466
/// the snapshot has already been created in that case.
6567
/// Expects a snapshot of the state where wasm runtime is loaded, but no guest module code is loaded.
6668
pub(super) fn new_from_loaded(
67-
mut loaded: MultiUseSandbox,
69+
loaded: MultiUseSandbox,
6870
snapshot: Arc<Snapshot>,
6971
) -> Result<Self> {
70-
loaded.restore(snapshot.clone())?;
7172
metrics::gauge!(METRIC_ACTIVE_WASM_SANDBOXES).increment(1);
7273
metrics::counter!(METRIC_TOTAL_WASM_SANDBOXES).increment(1);
7374
Ok(WasmSandbox {
7475
inner: Some(loaded),
7576
snapshot: Some(snapshot),
77+
needs_restore: true,
7678
})
7779
}
7880

81+
fn restore_if_needed(&mut self) -> Result<()> {
82+
if self.needs_restore {
83+
self.inner
84+
.as_mut()
85+
.ok_or(new_error!("WasmSandbox is none"))?
86+
.restore(
87+
self.snapshot
88+
.as_ref()
89+
.ok_or(new_error!("Snapshot is none"))?
90+
.clone(),
91+
)?;
92+
self.needs_restore = false;
93+
}
94+
Ok(())
95+
}
96+
7997
/// Load a Wasm module at the given path into the sandbox and return a `LoadedWasmSandbox`
8098
/// able to execute code in the loaded Wasm Module.
8199
///
82100
/// Before you can call guest functions in the sandbox, you must call
83101
/// this function and use the returned value to call guest functions.
84102
pub fn load_module(mut self, file: impl AsRef<Path>) -> Result<LoadedWasmSandbox> {
103+
self.restore_if_needed()?;
85104
let inner = self
86105
.inner
87106
.as_mut()
@@ -97,6 +116,18 @@ impl WasmSandbox {
97116
self.finalize_module_load()
98117
}
99118

119+
/// Load a Wasm module by restoring a Hyperlight snapshot taken
120+
/// from a `LoadedWasmSandbox`.
121+
pub fn load_from_snapshot(mut self, snapshot: Arc<Snapshot>) -> Result<LoadedWasmSandbox> {
122+
let sb = self
123+
.inner
124+
.as_mut()
125+
.ok_or_else(|| new_error!("WasmSandbox is None"))?;
126+
sb.restore(snapshot)?;
127+
128+
self.finalize_module_load()
129+
}
130+
100131
/// Load a Wasm module that is currently present in a buffer in
101132
/// host memory, by mapping the host memory directly into the
102133
/// sandbox.
@@ -114,6 +145,7 @@ impl WasmSandbox {
114145
base: *mut libc::c_void,
115146
len: usize,
116147
) -> Result<LoadedWasmSandbox> {
148+
self.restore_if_needed()?;
117149
let inner = self
118150
.inner
119151
.as_mut()
@@ -142,6 +174,7 @@ impl WasmSandbox {
142174
/// Before you can call guest functions in the sandbox, you must call
143175
/// this function and use the returned value to call guest functions.
144176
pub fn load_module_from_buffer(mut self, buffer: &[u8]) -> Result<LoadedWasmSandbox> {
177+
self.restore_if_needed()?;
145178
let inner = self
146179
.inner
147180
.as_mut()
@@ -473,6 +506,46 @@ mod tests {
473506
}
474507
}
475508

509+
#[test]
510+
fn test_load_from_snapshot() {
511+
let mut sandbox = SandboxBuilder::new().build().unwrap();
512+
sandbox
513+
.register(
514+
"GetTimeSinceBootMicrosecond",
515+
get_time_since_boot_microsecond,
516+
)
517+
.unwrap();
518+
let sb = sandbox.load_runtime().unwrap();
519+
520+
let helloworld_wasm = get_test_file_path("HelloWorld.aot").unwrap();
521+
let runwasm_wasm = get_test_file_path("RunWasm.aot").unwrap();
522+
523+
// load one module, and make sure that a function in it
524+
// can be called
525+
let mut lb1 = sb.load_module(helloworld_wasm).unwrap();
526+
let result: i32 = lb1
527+
.call_guest_function("HelloWorld", "Message from Rust Test".to_string())
528+
.unwrap();
529+
assert_eq!(result, 0);
530+
let snapshot = lb1.snapshot().unwrap();
531+
532+
// load another module, and make sure that a function in
533+
// it can be called
534+
let sb = lb1.unload_module().unwrap();
535+
let mut lb2 = sb.load_module(runwasm_wasm).unwrap();
536+
let result: i32 = lb2.call_guest_function("CalcFib", 10i32).unwrap();
537+
assert_eq!(result, 55);
538+
539+
// reload the first module via snapshot, and make sure the
540+
// original function can be called again
541+
let sb = lb2.unload_module().unwrap();
542+
let mut lb3 = sb.load_from_snapshot(snapshot).unwrap();
543+
let result: i32 = lb3
544+
.call_guest_function("HelloWorld", "Message from Rust Test".to_string())
545+
.unwrap();
546+
assert_eq!(result, 0);
547+
}
548+
476549
#[test]
477550
fn test_load_module_buffer() {
478551
let sandboxes = get_test_wasm_sandboxes().unwrap();

0 commit comments

Comments
 (0)