From 5b7e784c5c6b5858c818dd0daa4feedae550004a Mon Sep 17 00:00:00 2001 From: Wodann Date: Fri, 18 Oct 2019 15:49:25 +0200 Subject: [PATCH] feat(runtime): add extension to the Result type that allows retrying and waiting for a correct result --- crates/mun/src/main.rs | 3 +- crates/mun_runtime/src/assembly.rs | 13 ++-- crates/mun_runtime/src/lib.rs | 38 ++++++---- crates/mun_runtime/src/macros.rs | 111 ++++++++++++++++++++++------- crates/mun_runtime/src/test.rs | 10 +-- 5 files changed, 124 insertions(+), 51 deletions(-) diff --git a/crates/mun/src/main.rs b/crates/mun/src/main.rs index fdfb236f7..8f708044f 100644 --- a/crates/mun/src/main.rs +++ b/crates/mun/src/main.rs @@ -1,6 +1,7 @@ #[macro_use] extern crate failure; +use std::error::Error; use std::time::Duration; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; @@ -89,7 +90,7 @@ fn start(matches: &ArgMatches) -> Result<(), failure::Error> { let entry_point = matches.value_of("entry").unwrap_or("main"); #[allow(clippy::unit_arg)] - Ok(invoke_fn!(runtime, entry_point)) + invoke_fn!(runtime, entry_point).map_err(|e| failure::err_msg(e.description().to_string())) } fn compiler_options(matches: &ArgMatches) -> Result { diff --git a/crates/mun_runtime/src/assembly.rs b/crates/mun_runtime/src/assembly.rs index 76c3c7289..f3dd225ea 100644 --- a/crates/mun_runtime/src/assembly.rs +++ b/crates/mun_runtime/src/assembly.rs @@ -10,7 +10,7 @@ use self::temp_library::TempLibrary; use libloading::Symbol; use std::io; -/// An assembly is the smallest compilable unit of code in Mun. +/// An assembly is a hot reloadable compilation unit, consisting of one or more Mun modules. pub struct Assembly { library_path: PathBuf, library: Option, @@ -18,7 +18,7 @@ pub struct Assembly { } impl Assembly { - /// Loads an assembly for the library at `library_path` and its dependencies. + /// Loads an assembly and its information for the shared library at `library_path`. pub fn load( library_path: &Path, runtime_dispatch_table: &mut DispatchTable, @@ -43,6 +43,7 @@ impl Assembly { }) } + /// Links the assembly using the runtime's dispatch table. pub fn link(&mut self, runtime_dispatch_table: &DispatchTable) -> Result<(), Error> { for (dispatch_ptr, fn_signature) in self.info.dispatch_table.iter_mut() { let fn_ptr = runtime_dispatch_table @@ -61,6 +62,7 @@ impl Assembly { Ok(()) } + /// Swaps the assembly's shared library and its information for the library at `library_path`. pub fn swap( &mut self, library_path: &Path, @@ -68,7 +70,8 @@ impl Assembly { ) -> Result<(), Error> { // let library_path = library_path.canonicalize()?; - // Drop the old library, as some operating systems don't allow editing of in-use shared libraries + // Drop the old library, as some operating systems don't allow editing of in-use shared + // libraries self.library.take(); for function in self.info.symbols.functions() { @@ -80,12 +83,12 @@ impl Assembly { Ok(()) } - /// Retrieves the assembly's loaded shared library. + /// Returns the assembly's information. pub fn info(&self) -> &AssemblyInfo { &self.info } - /// Returns the path corresponding tot the assembly's library. + /// Returns the path corresponding to the assembly's library. pub fn library_path(&self) -> &Path { self.library_path.as_path() } diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index d32d7f077..a7806e439 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -47,6 +47,7 @@ impl RuntimeBuilder { } } +/// A runtime dispatch table that maps full function paths to function information. pub struct DispatchTable { functions: HashMap, } @@ -157,18 +158,29 @@ impl MunRuntime { } } +/// Extends a result object with functions that allow retrying of an action. +pub trait Retriable: Sized { + type Output; + + /// Retries an action, resulting in a potentially mutated version of itself. + fn retry(self) -> Self; + + /// Keeps retrying the same action until it succeeds, resulting in an output. + fn wait(self) -> Self::Output; +} + invoke_fn_impl! { - fn invoke_fn0(); - fn invoke_fn1(a: A); - fn invoke_fn2(a: A, b: B); - fn invoke_fn3(a: A, b: B, c: C); - fn invoke_fn4(a: A, b: B, c: C, d: D); - fn invoke_fn5(a: A, b: B, c: C, d: D, e: E); - fn invoke_fn6(a: A, b: B, c: C, d: D, e: E, f: F); - fn invoke_fn7(a: A, b: B, c: C, d: D, e: E, f: F, g: G); - fn invoke_fn8(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H); - fn invoke_fn9(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I); - fn invoke_fn10(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J); - fn invoke_fn11(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K); - fn invoke_fn12(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L); + fn invoke_fn0() -> InvokeErr0; + fn invoke_fn1(a: A) -> InvokeErr1; + fn invoke_fn2(a: A, b: B) -> InvokeErr2; + fn invoke_fn3(a: A, b: B, c: C) -> InvokeErr3; + fn invoke_fn4(a: A, b: B, c: C, d: D) -> InvokeErr4; + fn invoke_fn5(a: A, b: B, c: C, d: D, e: E) -> InvokeErr5; + fn invoke_fn6(a: A, b: B, c: C, d: D, e: E, f: F) -> InvokeErr6; + fn invoke_fn7(a: A, b: B, c: C, d: D, e: E, f: F, g: G) -> InvokeErr7; + fn invoke_fn8(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) -> InvokeErr8; + fn invoke_fn9(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) -> InvokeErr9; + fn invoke_fn10(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) -> InvokeErr10; + fn invoke_fn11(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K) -> InvokeErr11; + fn invoke_fn12(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L) -> InvokeErr12; } diff --git a/crates/mun_runtime/src/macros.rs b/crates/mun_runtime/src/macros.rs index 599897c7d..e37815475 100644 --- a/crates/mun_runtime/src/macros.rs +++ b/crates/mun_runtime/src/macros.rs @@ -1,38 +1,95 @@ macro_rules! invoke_fn_impl { ($( - fn $FnName:ident($($Arg:tt: $T:ident),*); + fn $FnName:ident($($Arg:tt: $T:ident),*) -> $ErrName:ident; )+) => { $( + /// An invocation error that contains the function name, a mutable reference to the + /// runtime, passed arguments, and the output type. This allows the caller to retry + /// the function invocation using the `Retriable` trait. + pub struct $ErrName<'r, 's, $($T: Reflection,)* Output:Reflection> { + msg: String, + runtime: &'r mut MunRuntime, + function_name: &'s str, + $($Arg: $T,)* + output: core::marker::PhantomData, + } + + impl<'r, 's, $($T: Reflection,)* Output: Reflection> core::fmt::Debug for $ErrName<'r, 's, $($T,)* Output> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", &self.msg) + } + } + + impl<'r, 's, $($T: Reflection,)* Output: Reflection> core::fmt::Display for $ErrName<'r, 's, $($T,)* Output> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", &self.msg) + } + } + + impl<'r, 's, $($T: Reflection,)* Output: Reflection> std::error::Error for $ErrName<'r, 's, $($T,)* Output> { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } + } + + impl<'r, 's, $($T: Reflection,)* Output: Reflection> $ErrName<'r, 's, $($T,)* Output> { + /// Constructs a new invocation error. + pub fn new(err_msg: String, runtime: &'r mut MunRuntime, function_name: &'s str, $($Arg: $T),*) -> Self { + Self { + msg: err_msg, + runtime, + function_name, + $($Arg,)* + output: core::marker::PhantomData, + } + } + } + + impl<'r, 's, $($T: Reflection,)* Output: Reflection> $crate::Retriable for core::result::Result> { + type Output = Output; + + fn retry(self) -> Self { + match self { + Ok(output) => Ok(output), + Err(err) => { + eprintln!("{}", err.msg); + while !err.runtime.update() { + // Wait until there has been an update that might fix the error + } + err.runtime.$FnName(err.function_name, $(err.$Arg,)*) + } + } + } + + fn wait(mut self) -> Self::Output { + loop { + if let Ok(output) = self { + return output; + } + self = self.retry(); + } + } + } + impl MunRuntime { - /// Invokes the method `method_name` with arguments `args`, in the library compiled based on - /// the manifest at `manifest_path`. + /// Invokes the method `method_name` with arguments `args`, in the library compiled + /// based on the manifest at `manifest_path`. /// - /// If an error occurs when invoking the method, an error message is logged. The runtime - /// continues looping until the cause of the error has been resolved. - pub fn $FnName<$($T: Reflection,)* Output: Reflection>( - &mut self, - function_name: &str, + /// If an error occurs when invoking the method, an error message is logged. The + /// runtime continues looping until the cause of the error has been resolved. + pub fn $FnName<'r, 's, $($T: Reflection,)* Output: Reflection>( + &'r mut self, + function_name: &'s str, $($Arg: $T,)* - ) -> Output { - // Initialize `updated` to `true` to guarantee the method is run at least once - let mut updated = true; - loop { - if updated { - let function: core::result::Result Output, String> = self - .get_function_info(function_name) - .ok_or(format!("Failed to obtain function '{}'", function_name)) - .and_then(|function| mun_abi::downcast_fn!(function, fn($($T),*) -> Output)); + ) -> core::result::Result> { + let function: core::result::Result Output, String> = self + .get_function_info(function_name) + .ok_or(format!("Failed to obtain function '{}'", function_name)) + .and_then(|function| mun_abi::downcast_fn!(function, fn($($T),*) -> Output)); - match function { - Ok(function) => return function($($Arg),*), - Err(ref e) => { - eprintln!("{}", e); - updated = false; - } - } - } else { - updated = self.update(); - } + match function { + Ok(function) => Ok(function($($Arg),*)), + Err(e) => Err($ErrName::new(e, self, function_name, $($Arg),*)), } } } diff --git a/crates/mun_runtime/src/test.rs b/crates/mun_runtime/src/test.rs index 76b1a9978..9a2629e48 100644 --- a/crates/mun_runtime/src/test.rs +++ b/crates/mun_runtime/src/test.rs @@ -37,7 +37,7 @@ fn compile_and_run() { ", ); let mut runtime = compile_result.new_runtime(); - let _result: () = invoke_fn!(runtime, "main"); + let _result: () = invoke_fn!(runtime, "main").unwrap(); } #[test] @@ -48,7 +48,7 @@ fn return_value() { ", ); let mut runtime = compile_result.new_runtime(); - let result: i64 = invoke_fn!(runtime, "main"); + let result: i64 = invoke_fn!(runtime, "main").unwrap(); assert_eq!(result, 3); } @@ -62,7 +62,7 @@ fn arguments() { let mut runtime = compile_result.new_runtime(); let a: i64 = 52; let b: i64 = 746; - let result: i64 = invoke_fn!(runtime, "main", a, b); + let result: i64 = invoke_fn!(runtime, "main", a, b).unwrap(); assert_eq!(result, a + b); } @@ -78,11 +78,11 @@ fn dispatch_table() { let a: i64 = 52; let b: i64 = 746; - let result: i64 = invoke_fn!(runtime, "main", a, b); + let result: i64 = invoke_fn!(runtime, "main", a, b).unwrap(); assert_eq!(result, a + b); let a: i64 = 6274; let b: i64 = 72; - let result: i64 = invoke_fn!(runtime, "add", a, b); + let result: i64 = invoke_fn!(runtime, "add", a, b).unwrap(); assert_eq!(result, a + b); }