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 host env via trait with proc macro #1739

Merged
merged 47 commits into from
Dec 1, 2020
Merged

Conversation

MarkMcCaskey
Copy link
Contributor

@MarkMcCaskey MarkMcCaskey commented Oct 20, 2020

This PR includes a trait-based host-env init and deinit flow (including the plumbing to call these functions) as well as a procedural macro to generate this code.

For example,

/// The environment provided to the WASI imports.
#[derive(Debug, Clone, WasmerEnv)]
pub struct WasiEnv {
    state: Arc<Mutex<WasiState>>,
    #[wasmer(export)]
    memory: LazyInit<Memory>,
    #[wasmer(export(name = "real_name"))]
    sum: LazyInit<NativeFunc<(i32, i32), i32>>,
}

will generate code roughly looking like:

impl ::wasmer::WasmerEnv for WasiEnv {
    fn finish(&mut self, instance: &::wasmer::Instance) {
        let memory: Memory = instance.exports.get_with_generics("memory").unwrap();
        self.memory.initialize(memory);
        let sum: NativeFunc<(i32, i32), i32> = instance.exports.get_with_generics("real_name").unwrap();
        self.sum.initialize(sum);
    }
}
impl WasiEnv {
    pub fn memory_ref(&self) -> Option<&Memory> {
        self.memory.get()
    }
    pub fn sum_ref(&self) -> Option<&NativeFunc<(i32, i32), i32>> {
        self.sum.get()
    }
    pub unsafe fn memory_ref_unchecked(&self) -> &Memory {
        self.memory.get_unchecked()
    }
    pub unsafe fn sum_ref_unchecked(&self) -> &NativeFunc<(i32, i32), i32> {
        self.sum.get_unchecked()
    }
}

The exact details of the interface are subject to change and should be reviewed thoroughly, for example we will want to be able to handle errors properly instead of unwraping and assuming they don't happen.

note to self: The generated helper functions can be used in an unsound way if called before we instantiate, but it'd be nice to not have to use unsafe or deal with Option when being called from a host function...

Review

  • Review lazy init abstraction
    • Review its thread-safety
    • Review its interface
  • Review the WasmerEnv trait
    • Review the interface
    • Review the plumbing (function pointer casting, data flow, requirement that all types implement WasmerEnv)
  • Review the proc macro
    • Review the implementation, likely simpler ways to do certain things but proc macros are kind of confusing
    • Review test coverage
    • Review test coverage of unhappy paths / error messages, etc
  • Add a short description of the the change to the CHANGELOG.md file

@MarkMcCaskey MarkMcCaskey changed the title Add hacked together vertical slice of host env updating idea Add host env via trait prototype Oct 20, 2020
@wasmerio wasmerio deleted a comment from bors bot Oct 20, 2020
bors bot added a commit that referenced this pull request Oct 20, 2020
@wasmerio wasmerio deleted a comment from bors bot Oct 21, 2020
bors bot added a commit that referenced this pull request Oct 21, 2020
@wasmerio wasmerio deleted a comment from bors bot Oct 21, 2020
bors bot added a commit that referenced this pull request Oct 21, 2020
@wasmerio wasmerio deleted a comment from bors bot Oct 21, 2020
Copy link
Contributor

@nlewycky nlewycky left a comment

Choose a reason for hiding this comment

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

Could you break the vmctx -> union part to its own PR?

@MarkMcCaskey MarkMcCaskey changed the title Add host env via trait prototype Add host env via trait with proc macro Oct 23, 2020
@MarkMcCaskey MarkMcCaskey marked this pull request as ready for review October 23, 2020 21:03
examples/wasi.rs Show resolved Hide resolved
lib/api/src/externals/function.rs Outdated Show resolved Hide resolved
lib/api/src/lib.rs Outdated Show resolved Hide resolved
lib/api/src/lib.rs Outdated Show resolved Hide resolved
lib/api/src/lib.rs Outdated Show resolved Hide resolved
}
}

unsafe impl<T: Send> Send for InitAfterInstance<T> {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why InitAfterInstance must implement Send? Is it to preserve the behavior of T in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was also for WasiEnv but more largely because the way Wasmer works, we can pass functions to other threads and call it meaning that it's Send... perhaps it can also be used in a Sync way but I didn't think so...

Someone filed an issue for us to review this and I agree, that we should probably audit what we actually allow users to do

lib/derive/src/lib.rs Outdated Show resolved Hide resolved
lib/engine/src/artifact.rs Outdated Show resolved Hide resolved
@@ -31,6 +31,8 @@ pub struct ExportFunction {
pub address: *const VMFunctionBody,
/// Pointer to the containing `VMContext`.
pub vmctx: *mut VMContext,
/// temp code to set vmctx for host functions
pub function_ptr: Option<fn(*mut std::ffi::c_void, *const std::ffi::c_void)>,
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand what it does exactly, and why the name function_ptr? Can you explain please?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is WasmerEnv::finish for the env used by this function, due to our heirarchy of types, we can't be explicit about the type signature because this is in vm and it takes &api::Instance as an argument.

It's really not ideal but I haven't come up with a better way to get access to the initializer function yet. This is the thing I was hoping to get the most feedback on early

Copy link
Member

Choose a reason for hiding this comment

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

I'm unclear on why we need this here. The env finisher should live in the InstanceHandle, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question!

This is how the function pointers get into the InstanceHandle: we lose type information, but wasmer::externals::function::Function methods have access to the initializer, we have to store it somewhere for later and this is the best place I've found.

The little code window above that Ivan commented on is showing out of date code, but I left a comment there asking for feedback if anyone knows a better way to get this information to where we need it.

Copy link
Member

Choose a reason for hiding this comment

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

Why don't store it in InstanceHandle ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do, but we have to get that information to the InstanceHandle.

The data flow starts when a user makes a new Function:

  1. Function::new_with_env : by way of the trait constraint, we can get the initializer and turn it into a function pointer. We then put it in ExportFunction because it seemed like the most obvious place to put it.

  2. We need this data when instantiating Instance::new, Module::instantiate , we only get a module and a resolver (the resolver is where all the functions are living)

  3. We end up in engine::Artifact::instantiate here we extract these values from the Imports during instantiation and make it part of the InstanceHandle

  4. Later we call a function on InstanceHandle to do something with this stored data

lib/vm/src/vmcontext.rs Outdated Show resolved Hide resolved
Copy link
Member

@syrusakbary syrusakbary left a comment

Choose a reason for hiding this comment

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

The PR I think it moves to the good direction.

Feedback:

  1. Test the wasm-c-api wasm_func_new_with_env with a custom finalizer
  2. Test the behavior of a creating a custom environment. That the finalizer and free methods get called.

@@ -140,6 +143,12 @@ impl Function {
let vmctx = VMFunctionEnvironment {
host_env: Box::into_raw(Box::new(dynamic_ctx)) as *mut _,
};
// TODO: look into removing transmute by changing API type signatures
Copy link
Member

Choose a reason for hiding this comment

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

How we can change the API?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There's no good way to do this that I can think of yet. The only way to avoid the transmutes would be to to make the WasmerEnv::init_with_instance function take void* directly... but that seems like a bad trade off.

@jubianchi jubianchi added the 1.0 Wasmer at 1.0 label Nov 20, 2020
Comment on lines 307 to 311
// TODO: look into removing transmute by changing API type signatures
let function_ptr = Some(std::mem::transmute::<
fn(_, _) -> Result<(), _>,
fn(_, _) -> Result<(), _>,
>(Env::init_with_instance));
Copy link
Member

Choose a reason for hiding this comment

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

I'm not super sure why this is needed. Can you provide some context @MarkMcCaskey ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Which part? The function_ptr at all or the transmute?

We need the function pointer to initialize the data, so far this is the most direct place to put it so we can access where we need it.

We have to transmute it because WasmerEnv isn't known to the vm so we can't use the real name of the type, I have some work in progress trying to make it so vm can see WasmerEnv but it's a bit stuck

@@ -291,6 +317,7 @@ impl Function {
address,
kind: VMFunctionKind::Static,
vmctx,
function_ptr,
Copy link
Member

Choose a reason for hiding this comment

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

We should move this out of this struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the same as #1739 (comment) ; it may be possible but I'm not exactly sure how yet

Comment on lines 71 to 72
// TODO:
function_ptr: None,
Copy link
Member

Choose a reason for hiding this comment

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

I really think this doesn't belong here.

/// instantiating the module.
#[derive(Debug)]
pub(crate) struct FunctionWrapper {
pub(crate) func: NonNull<Function>,
pub(crate) legacy_env: NonNull<UnsafeMutableEnv>,
pub(crate) legacy_env: NonNull<LegacyEnv>,
Copy link
Member

@syrusakbary syrusakbary Nov 23, 2020

Choose a reason for hiding this comment

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

I actually like more UnsafeMutableLegacyEnv. Verbose but it conveys the meaning. MutableLegacyEnv is also good

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So this change is actually fixing an issue from the find and replace done on in another PR. It should really be split out and merged to master separately, but I accidentally renamed a struct with the same name in another place by doing bulk find and replace, this just corrects that mistake

lib/wasi/src/lib.rs Outdated Show resolved Hide resolved
@MarkMcCaskey
Copy link
Contributor Author

bors r+

@bors
Copy link
Contributor

bors bot commented Dec 1, 2020

@bors bors bot merged commit 6041c03 into master Dec 1, 2020
@bors bors bot deleted the feature/host-env-prototype branch December 1, 2020 01:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1.0 Wasmer at 1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants