-
Notifications
You must be signed in to change notification settings - Fork 824
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 Table example; fix local Table and Memory metadata ownership #1687
Conversation
Thanks for working on this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't forget to update examples/README.md
.
This commit contains a ton of debug code that we'll need to remove
bors try |
tryBuild succeeded: |
#[derive(Debug)] | ||
enum VMMemoryDefinitionOwnership { | ||
/// The `VMMemoryDefinition` is owned by the `Instance` and we should use | ||
/// its memory. This is how a local memory that's exported should be stored. | ||
VMOwned(NonNull<VMMemoryDefinition>), | ||
/// The `VMMemoryDefinition` is owned by the host and we should manage its | ||
/// memory. This is how an imported memory that doesn't come from another | ||
/// Wasm module should be stored. | ||
HostOwned(Box<UnsafeCell<VMMemoryDefinition>>), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not super fan of this division. A memory is a memory, regardless of who owns it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is encapsulated internally and the reason we store it this way is so that freeing memory works correctly:
VMMemoryDefinitionOwnership::VMOwned
won't free the memory when it gets dropped but VMMemoryDefinitionOwnership::HostOwned
will.
I agree that it might make sense to define methods on this type to avoid having to match
in the implementation code.
Let me know if that's not what you had in mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hypothetically if we don't fix it this way, then here's another thing we could do, not that it wouldn't be a ton of work. Make sure that VM* objects exist only inside the VMContext/Instance. wasmer_api:: would need two objects for memory, one that would be a handle to any kind of memory and the other which would be a host memory (detached from instance/vmcontext) and would contain the backing store. The VMMemoryImport would no longer point to a VMMemoryDefinition because a host Memory wouldn't have one, it would have to point to all the relevant parts (the backing, the enough to call grow and size, etc.) directly. The user has to keep the host memory and backing store alive, probably by Rc or Arc, and we could somewhat fold it into the handle (api::Memory) with an enum that indicates whether it's holding a whole backing store, or pointing to a VMMemoryDefinition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea being that VMMemoryImport
would never own its metadata?
I'm not sure I fully understood what you're suggesting but to make sure we're on the same page:
- The generated code always needs a
VMMemoryDefinition
.- If it's local, it lives in VM-owned memory
- if it's imported it lives somewhere else (either another VM's memory or the host)
- In the current code, due to
Arc
, an Instance using an imported host type will ensure that it's never freed while the Instance is using it. - We need an extra layer of indirection for imported VM Objects to make sure that modifications are reflected in the generated code. It's possible to remove this layer of indirection but it means we'd need update logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, the idea being that VMMemoryImport would never own its metadata. In this model the generated code would need to change to work with a VMMemoryImport.
The compiler-generated code needs to know the base pointer and the size. It also has to be able to grow and size, both of which go through libcalls (even in the local owned memory case). More subtly we need invalidation rules for the base pointer and the size, for instance can we assume they never change at any time the function is executing?
One possible solution. You could have the VMMemoryImport have ptr_to_ versions of the fields the VMMemoryDefinition has. Instead of ptr-to-vmmemorydefinition and arc it would have a ptr_to_base and ptr_to_current_length, then the Arc (or whatever we call the object that keeps the pointed-to-memory alive, if anything) goes in the Instance::memories, not the VMContext objects.
The important part of my suggestion is to start with a rule that VM* objects are only ever part of a VMContext and then follow that through the rest of the design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The important part of my suggestion is to start with a rule that VM* objects are only ever part of a VMContext and then follow that through the rest of the design.
I think this makes ownership harder in the case of a VM object imported into multiple instances, who is responsible for freeing it? What about memories created when there are no Instances created?
I'm not necessarily opposed to the changes but I think we should land this PR first, this change is self-contained and fixes user-facing bugs; the implementation can easily be changed later in my opinion.
I did a very quick review, but discussion happens on Slack, easier for the moment. |
let num_memories = offsets.num_local_memories; | ||
let num_memories = usize::try_from(num_memories).unwrap(); | ||
let mut out = Vec::with_capacity(num_memories); | ||
// TODO: better encapsulate this logic, this shouldn't be duplicated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(No action required:) I feel like this is a great example of a TODO that shouldn't have a bug filed.
#[derive(Debug)] | ||
enum VMMemoryDefinitionOwnership { | ||
/// The `VMMemoryDefinition` is owned by the `Instance` and we should use | ||
/// its memory. This is how a local memory that's exported should be stored. | ||
VMOwned(NonNull<VMMemoryDefinition>), | ||
/// The `VMMemoryDefinition` is owned by the host and we should manage its | ||
/// memory. This is how an imported memory that doesn't come from another | ||
/// Wasm module should be stored. | ||
HostOwned(Box<UnsafeCell<VMMemoryDefinition>>), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hypothetically if we don't fix it this way, then here's another thing we could do, not that it wouldn't be a ton of work. Make sure that VM* objects exist only inside the VMContext/Instance. wasmer_api:: would need two objects for memory, one that would be a handle to any kind of memory and the other which would be a host memory (detached from instance/vmcontext) and would contain the backing store. The VMMemoryImport would no longer point to a VMMemoryDefinition because a host Memory wouldn't have one, it would have to point to all the relevant parts (the backing, the enough to call grow and size, etc.) directly. The user has to keep the host memory and backing store alive, probably by Rc or Arc, and we could somewhat fold it into the handle (api::Memory) with an enum that indicates whether it's holding a whole backing store, or pointing to a VMMemoryDefinition.
1752: Add more examples for the Rust API r=syrusakbary a=jubianchi # Description The goal is to cover most of the Rust API with examples (and thus tests). Some other PRs already exist for that purpose: * #1730 * #1687 # Review - [ ] Add a short description of the the change to the CHANGELOG.md file Co-authored-by: jubianchi <[email protected]>
bors r+ |
Build succeeded: |
Adds an example using the Table API.
Work in progress, debugging an issue with growing using the Table API...
Things I'd like to have before shipping:
Review