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

DO NOT MERGE: Prototype some potential error handling ways #192

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

dignifiedquire
Copy link
Contributor

Split by commits

  1. Some utilities to make the code dealing with errors nicer in general
  2. Introduce a Abort error that calls rt::abort when converted into, allowing a very explicit triggering via type conversion on the highlevel methods, while still being able to use ActorError in all internal methods without triggering aborts.

@@ -34,25 +34,23 @@ pub enum Method {
pub struct Actor;
impl Actor {
/// Constructor for Account actor
pub fn constructor<BS, RT>(rt: &mut RT, address: Address) -> Result<(), ActorError>
pub fn constructor<BS, RT>(rt: &mut RT, address: Address) -> Result<(), Abort>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that Abort can't happen, why not just return ()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I wanted to potential for actually returning an error in debug scenarios. My idea was that we could have sth like a debug feature, where Abort would collect data and actually be a returned value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One alternative design I had was

fn foo() -> Abort<T> {}

type Abort<T> = Result<T, AbortError>;

to make it even more visual that this is a special kind of exit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I wanted to potential for actually returning an error in debug scenarios. My idea was that we could have sth like a debug feature, where Abort would collect data and actually be a returned value.

Ok, that's reasonable. Although, IMO, we get better information if we abort immediately (and ask wasmtime for a backtrace). See https://docs.wasmtime.dev/api/wasmtime/struct.Config.html#method.wasm_backtrace_details.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some scope for divergent behaviour between the two cases here. I guess that this Abort is intended to always be immediately propagated with ?.

Up at this high level of the actor entry points, I would probably lean towards Steb's initial take that actually aborting is better. We would want a way to shim that call for testing, though, wrapping out the underlying static call so we can use a mock/fake VM instead.

@Stebalien
Copy link
Member

I like the usability, but I also kind of hate it because things that look like they should return an error, actually just fail.

How about just adding a panic handler? That way we can add .unwrap() things.

Or add a custom unwrap_or_abort method where we want to abort? That's a lot more explicit.

@dignifiedquire
Copy link
Contributor Author

That way we can add .unwrap() things.

The reason I don't want to use unwrap is that for me that is a sign of "Error" that should never happen unless there is a serious bug. So I would like to not conflate this

@dignifiedquire
Copy link
Contributor Author

Or add a custom unwrap_or_abort method where we want to abort? That's a lot more explicit.

If we can find a shorter name I would be okay with an explicit call, but I am not fully convinced it is actually better, but I understand your confliction.

@Stebalien
Copy link
Member

If we can find a shorter name I would be okay with an explicit call, but I am not fully convinced it is actually better, but I understand your confliction.

.yeet()

But actually, we can't do that because that's what we're calling async sends in future FVM versions. Decisions have been made.

@dignifiedquire
Copy link
Contributor Author

I guess we are sticking with ? then :P

@@ -34,25 +34,23 @@ pub enum Method {
pub struct Actor;
impl Actor {
/// Constructor for Account actor
pub fn constructor<BS, RT>(rt: &mut RT, address: Address) -> Result<(), ActorError>
pub fn constructor<BS, RT>(rt: &mut RT, address: Address) -> Result<(), Abort>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some scope for divergent behaviour between the two cases here. I guess that this Abort is intended to always be immediately propagated with ?.

Up at this high level of the actor entry points, I would probably lean towards Steb's initial take that actually aborting is better. We would want a way to shim that call for testing, though, wrapping out the underlying static call so we can use a mock/fake VM instead.

@@ -82,7 +80,7 @@ impl ActorCode for Actor {
let addr = Self::pubkey_address(rt)?;
Ok(RawBytes::serialize(addr)?)
}
None => Err(actor_error!(SysErrInvalidMethod; "Invalid method")),
None => Err(actor_error!(SysErrInvalidMethod; "Invalid method").into()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get this is just prototyping, but I want only one of the ActorError or Abort types to survive. They're going after the same problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They definitely should survive both in this construction. The conversion into an Abort is what triggers the abort, where as ActorError still can be used inside an actor to communicate recoverable errors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think ActorError should be used for recoverable errors. I don't think there are use cases for that. I would say recoverable errors should not specify an abort code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where would that mapping to codes live then?

#[cfg(not(feature = "fil-actor"))]
fn maybe_abort(exit_code: ExitCode, msg: Option<&str>) -> ! {
// TODO: maybe not panic, needs discussion what we want here
panic!("Abort: {}: {:?}", exit_code, msg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works well in Go. If we can reliably catch the panic, e.g. in test harnesses, and inspect the exit code, then this could be good. My read of the Rust docs suggested this was even more frowned upon than in Go, though. cc @ZenGround0

I would make the msg non-optional.

macro_rules! ensure {
($cond:expr, $code:ident, $msg:literal $(,)?) => {
if !$cond {
return Err($crate::actor_error!($code, $msg).into());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious: why use into rather than just construct the Abort explicitly here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it is not possible to construct an Abort, only be triggering the into implementation. This does not necessarily convert into an Abort, it could just convert into itself or other Error types depending on where it is used.

@jennijuju
Copy link
Member

jennijuju commented Apr 11, 2022

(This will be a post M1 task - will be a part of the control flow immediate interruption on error_ epic to match go's behaviour

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants