Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an extension for result objects that allows retrying on failure #15

Merged
merged 1 commit into from
Oct 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/mun/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#[macro_use]
extern crate failure;

use std::error::Error;
use std::time::Duration;

use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
Expand Down Expand Up @@ -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<mun_compiler::CompilerOptions, failure::Error> {
Expand Down
13 changes: 8 additions & 5 deletions crates/mun_runtime/src/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ 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<TempLibrary>,
info: AssemblyInfo,
}

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,
Expand All @@ -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
Expand All @@ -61,14 +62,16 @@ 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,
runtime_dispatch_table: &mut DispatchTable,
) -> 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() {
Expand All @@ -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()
}
Expand Down
38 changes: 25 additions & 13 deletions crates/mun_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl RuntimeBuilder {
}
}

/// A runtime dispatch table that maps full function paths to function information.
pub struct DispatchTable {
functions: HashMap<String, FunctionInfo>,
}
Expand Down Expand Up @@ -157,18 +158,29 @@ impl MunRuntime {
}
}

/// Extends a result object with functions that allow retrying of an action.
pub trait Retriable: Sized {
Wodann marked this conversation as resolved.
Show resolved Hide resolved
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;
}
111 changes: 84 additions & 27 deletions crates/mun_runtime/src/macros.rs
Original file line number Diff line number Diff line change
@@ -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<Output>,
}

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<Output, $ErrName<'r, 's, $($T,)* Output>> {
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<fn($($T),*) -> 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<Output, $ErrName<'r, 's, $($T,)* Output>> {
let function: core::result::Result<fn($($T),*) -> 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),*)),
}
}
}
Expand Down
10 changes: 5 additions & 5 deletions crates/mun_runtime/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}