@@ -63,6 +63,7 @@ pub struct MultiUseSandbox {
6363 dispatch_ptr : RawPtr ,
6464 #[ cfg( gdb) ]
6565 dbg_mem_access_fn : DbgMemAccessHandlerWrapper ,
66+ snapshot : Option < Snapshot > ,
6667}
6768
6869impl MultiUseSandbox {
@@ -87,6 +88,7 @@ impl MultiUseSandbox {
8788 dispatch_ptr,
8889 #[ cfg( gdb) ]
8990 dbg_mem_access_fn,
91+ snapshot : None ,
9092 }
9193 }
9294
@@ -122,7 +124,7 @@ impl MultiUseSandbox {
122124 . unwrap_mgr_mut ( )
123125 . snapshot ( self . id , mapped_regions_vec) ?;
124126 Ok ( Snapshot {
125- inner : memory_snapshot,
127+ inner : Arc :: new ( memory_snapshot) ,
126128 } )
127129 }
128130
@@ -181,12 +183,15 @@ impl MultiUseSandbox {
181183 unsafe { self . vm . map_region ( region) ? } ;
182184 }
183185
186+ // The restored snapshot is now our most current snapshot
187+ self . snapshot = Some ( snapshot. clone ( ) ) ;
188+
184189 Ok ( ( ) )
185190 }
186191
187192 /// Calls a guest function by name with the specified arguments.
188193 ///
189- /// Changes made to the sandbox during execution are persisted.
194+ /// Changes made to the sandbox during execution are *not* persisted.
190195 ///
191196 /// # Examples
192197 ///
@@ -215,12 +220,65 @@ impl MultiUseSandbox {
215220 /// # Ok(())
216221 /// # }
217222 /// ```
223+ #[ doc( hidden) ]
224+ #[ deprecated(
225+ since = "0.8.0" ,
226+ note = "Deprecated in favour or call and snapshot/restore."
227+ ) ]
218228 #[ instrument( err( Debug ) , skip( self , args) , parent = Span :: current( ) ) ]
219229 pub fn call_guest_function_by_name < Output : SupportedReturnType > (
220230 & mut self ,
221231 func_name : & str ,
222232 args : impl ParameterTuple ,
223233 ) -> Result < Output > {
234+ let snapshot = match & self . snapshot {
235+ Some ( snapshot) => snapshot. clone ( ) ,
236+ None => self . snapshot ( ) ?,
237+ } ;
238+ let res = self . run ( func_name, args) ;
239+ self . restore ( & snapshot) ?;
240+ res
241+ }
242+
243+ /// Calls a guest function by name with the specified arguments.
244+ ///
245+ /// Changes made to the sandbox during execution are persisted.
246+ ///
247+ /// # Examples
248+ ///
249+ /// ```no_run
250+ /// # use hyperlight_host::{MultiUseSandbox, UninitializedSandbox, GuestBinary};
251+ /// # fn example() -> Result<(), Box<dyn std::error::Error>> {
252+ /// let mut sandbox: MultiUseSandbox = UninitializedSandbox::new(
253+ /// GuestBinary::FilePath("guest.bin".into()),
254+ /// None
255+ /// )?.evolve()?;
256+ ///
257+ /// // Call function with no arguments
258+ /// let result: i32 = sandbox.call("GetCounter", ())?;
259+ ///
260+ /// // Call function with single argument
261+ /// let doubled: i32 = sandbox.call("Double", 21)?;
262+ /// assert_eq!(doubled, 42);
263+ ///
264+ /// // Call function with multiple arguments
265+ /// let sum: i32 = sandbox.call("Add", (10, 32))?;
266+ /// assert_eq!(sum, 42);
267+ ///
268+ /// // Call function returning string
269+ /// let message: String = sandbox.call("Echo", "Hello, World!".to_string())?;
270+ /// assert_eq!(message, "Hello, World!");
271+ /// # Ok(())
272+ /// # }
273+ /// ```
274+ #[ instrument( err( Debug ) , skip( self , args) , parent = Span :: current( ) ) ]
275+ pub fn run < Output : SupportedReturnType > (
276+ & mut self ,
277+ func_name : & str ,
278+ args : impl ParameterTuple ,
279+ ) -> Result < Output > {
280+ // Reset snapshot since we are mutating the sandbox state
281+ self . snapshot = None ;
224282 maybe_time_and_emit_guest_call ( func_name, || {
225283 let ret = self . call_guest_function_by_name_no_reset (
226284 func_name,
@@ -429,9 +487,33 @@ mod tests {
429487 use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags , MemoryRegionType } ;
430488 #[ cfg( target_os = "linux" ) ]
431489 use crate :: mem:: shared_mem:: { ExclusiveSharedMemory , GuestSharedMemory , SharedMemory as _} ;
432- use crate :: sandbox:: { Callable , SandboxConfiguration } ;
490+ use crate :: sandbox:: { Callable as _ , SandboxConfiguration } ;
433491 use crate :: { GuestBinary , HyperlightError , MultiUseSandbox , Result , UninitializedSandbox } ;
434492
493+ /// Tests that call_guest_function_by_name restores the state correctly
494+ #[ test]
495+ fn test_call_guest_function_by_name ( ) {
496+ let mut sbox: MultiUseSandbox = {
497+ let path = simple_guest_as_string ( ) . unwrap ( ) ;
498+ let u_sbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None ) . unwrap ( ) ;
499+ u_sbox. evolve ( )
500+ }
501+ . unwrap ( ) ;
502+
503+ let snapshot = sbox. snapshot ( ) . unwrap ( ) ;
504+
505+ let _ = sbox. run :: < i32 > ( "AddToStatic" , 5i32 ) . unwrap ( ) ;
506+ let res: i32 = sbox. run ( "GetStatic" , ( ) ) . unwrap ( ) ;
507+ assert_eq ! ( res, 5 ) ;
508+
509+ sbox. restore ( & snapshot) . unwrap ( ) ;
510+ #[ allow( deprecated) ]
511+ let _ = sbox. call_guest_function_by_name :: < i32 > ( "AddToStatic" , 5i32 ) . unwrap ( ) ;
512+ #[ allow( deprecated) ]
513+ let res: i32 = sbox. call_guest_function_by_name ( "GetStatic" , ( ) ) . unwrap ( ) ;
514+ assert_eq ! ( res, 0 ) ;
515+ }
516+
435517 // Tests to ensure that many (1000) function calls can be made in a call context with a small stack (1K) and heap(14K).
436518 // This test effectively ensures that the stack is being properly reset after each call and we are not leaking memory in the Guest.
437519 #[ test]
@@ -481,15 +563,13 @@ mod tests {
481563
482564 let snapshot = sbox. snapshot ( ) . unwrap ( ) ;
483565
484- let _ = sbox
485- . call_guest_function_by_name :: < i32 > ( "AddToStatic" , 5i32 )
486- . unwrap ( ) ;
566+ let _ = sbox. run :: < i32 > ( "AddToStatic" , 5i32 ) . unwrap ( ) ;
487567
488- let res: i32 = sbox. call_guest_function_by_name ( "GetStatic" , ( ) ) . unwrap ( ) ;
568+ let res: i32 = sbox. run ( "GetStatic" , ( ) ) . unwrap ( ) ;
489569 assert_eq ! ( res, 5 ) ;
490570
491571 sbox. restore ( & snapshot) . unwrap ( ) ;
492- let res: i32 = sbox. call_guest_function_by_name ( "GetStatic" , ( ) ) . unwrap ( ) ;
572+ let res: i32 = sbox. run ( "GetStatic" , ( ) ) . unwrap ( ) ;
493573 assert_eq ! ( res, 0 ) ;
494574 }
495575
0 commit comments