-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Add reentrancy support to Witty #960
Conversation
linera-witty/tests/reentrancy.rs
Outdated
let instance = factory.load_test_module("reentrancy", "operations", |instance| { | ||
ExportedOperations::export_to(instance) | ||
.expect("Failed to export reentrant operations WIT interface") |
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 just guessing here but) Would it make sense to adjust the type of the lambda to be able to write this?
factory.load_test_module("reentrancy", "operations", ExportedOperations::export_to)
or perhaps this?
factory.load_test_module::<ExportedOperations>("reentrancy", "operations")
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 like the idea, thanks! The only thing is how to handle tests without any exports. I'll create a WithoutExports
dummy type and use it as the default type to see how it looks 👍
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.
Done.
witty_macros::test_modules::getters::*, | ||
}; | ||
|
||
struct Implementation; |
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.
Would it possible to use a global variable to demonstrate modifying a state in successive calls?
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 think so. I'll look into 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.
Done, at least I think? Let me know if the new test is what you were thinking.
linera-witty/tests/reentrancy.rs
Outdated
#[test_case(MockInstanceFactory::default(); "with a mock instance")] | ||
#[cfg_attr(feature = "wasmer", test_case(WasmerInstanceFactory; "with Wasmer"))] | ||
#[cfg_attr(feature = "wasmtime", test_case(WasmtimeInstanceFactory; "with Wasmtime"))] | ||
fn getters<InstanceFactory>(mut factory: InstanceFactory) |
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.
Can we start tests with test_
?
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 can do that, but I'm a bit hesitant to do so because the output will start looking like:
test test_getters::with_a_mock_instance ... ok
...
instead of just
test getters::with_a_mock_instance ... ok
...
What do you think?
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.
Well, the usual way to name functions is "do_something", and for tests, ideally "test_that_bla_does_this".
To me, "something" feels like a getter.
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.
For methods and normal functions I agree. But for test functions not so much, because the test functions aren't called by anybody explicitly.
Applied the suggestion because it doesn't matter much anyway 😅
pub struct ExportedSetters; | ||
|
||
#[wit_export(package = "witty-macros:test-modules", interface = "setters")] | ||
impl ExportedSetters { |
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.
Would it make sense to support this more concise variant?
pub struct ExportedSetters<Caller>(std::marker::PhantomData<Caller>));
impl Default for ExportedSetters<Caller> { ... }
#[wit_export(package = "witty-macros:test-modules", interface = "setters")]
impl<Caller> ExportedSetters<Caller>
where
Caller: InstanceForImportedSetters,
<Caller::Runtime as Runtime>::Memory: RuntimeMemory<Caller>,
{
fn set_bool(caller: Caller, value: bool) -> Result<(), RuntimeError> { ... }
// ..
}
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.
That would be better, thanks! I'll look into 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.
In a next PR right? (do we need a GH issue?)
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.
Well, I started doing it, but it's a bit more complex than I thought 😅 . I opened #963 to finish it later.
Prepare to allow using the instance types as callers inside reentrant functions.
Check if it has a first parameter with a generic type for receiving the caller instance.
Use only the WIT types in the parameters.
Make reentrant calls possible.
Ensure that the exported host functions can call the guest Wasm instance.
Import and export the same functions to allow the host to act as a proxy in order to test reentrancy.
Use the reentrancy test modules to test reentrancy with real Wasm runtimes.
Add a verb to most test names.
Replace the closure with a type parameter for the `ExportTo` implementation.
Improve debuggability of tests.
Allow some private state to be stored inside the handlers.
Check that a reentrant call back to the guest uses the same internal state from when the guest called the host.
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.
Nice work!
Motivation
One of the main goals of the Witty crate is to make it easy to export host functions to guest Wasm modules in a reentrant manner, i.e., allowing the host functions to call back to the guest before returning.
Proposal
Allow reentrant functions to be defined in
impl
blocks that use the#[wit_export]
attribute procedural macro by having the functions have a generic type parameter representing the Wasm runtime-specific caller instance. The macro automatically detects this and generates code that will handle the parameter in a special manner.Since the caller type has to be borrowed by the handler closure, the runtime instance traits must also be implemented for
&mut T
, not justT
anymore.Test Plan
Some new integration tests are included which have reentrant functions. These functions are called by the guest and act as proxies, calling back to the guest to complete the test.
The tests also cover fallible functions, covering the bug fixed by #959.
Links
Part of #906
Release Plan
Reviewer Checklist