-
Notifications
You must be signed in to change notification settings - Fork 48
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
Better Caller #208
Comments
I just tried sketching some of this out in code and it looks like it won't work. A |
I think I stumbled over exactly that same issue too when trying to avoid the A possible way I can think of to solve this, might be to define additional delegate types similar to the built-in public delegate void CallerAction(Caller caller);
public delegate void CallerAction<in T>(Caller caller, T arg);
public delegate void CallerAction<in T1, in T2>(Caller caller, T1 arg1, T2 arg2);
// (...)
public delegate TResult CallerFunc<out TResult>(Caller caller);
public delegate TResult CallerFunc<in T, out TResult>(Caller caller, T arg);
public delegate TResult CallerFunc<in T1, in T2, out TResult>(Caller caller, T1 arg1, T2 arg2);
// (...) Then, instead of public static Function FromCallback<T, TResult>(IStore store, Func<Caller, T?, TResult> callback) we would have public static Function FromCallback<T, TResult>(IStore store, CallerFunc<T?, TResult> callback) for the delegates that take a |
That's an interesting idea, thanks! I'll have a fiddle with it over the weekend to see if it looks like it'll work. |
I could imagine a possible alternative to the As far as I understand the So the That way, there would still be wrapper objects ( However, I don't know |
I did consider a caching scheme like that. However since the lifetime of things returned from the caller seems to be deliberately tied to the lifetime of the caller I assumed it wouldn't be ok. |
I experimented with this over the weekend and have it mostly working. Currently failing just 7 tests due to 3 Two of these are the However the final one is this method which invokes a method using reflection, it seems to be used as a fallback when the generic methods can't be used (for example too many arguments or too many return values). As far as I can see it's not possible to pass a The only solution I can see is simply to fail in the case that the fallback is used and a |
Closed with #214. |
For the last couple of days I've been contemplating how to improve the
Caller
system, what I've come up with entails a large refactor of how things are done so I thought I'd lay out my thoughts and get some feedback. @peterhuene and @kpreisser I'd really value your input :)Context
At the moment a callback from WASM into C# can optionally take a
Caller
parameter which gives the call some context (e.g. access toMemory
etc). For example:The Problem
At the moment this mechanism always allocates a
Caller
for every call and will also often be used to fetch aMemory
orFunction
which is in turn allocated. That's not great for performance (particularly in Unity, which is where I'm using wasmtime).The Solution?
The obvious solution to this is to make
Caller
aref struct
. This also better models the intended lifetime of theCaller
as well - you're not meant to hold it beyond that one method call.However there is a problem with this -
Caller
implementsIStore
and in turns passes itself intoMemory
andFunction
constructors as the store reference. Aref struct
cannot implement an interface so this doesn't work (it also can't be passed into a generic method, so we can't refactor to something likeInvoke<T> where T : IStore
either). So if we want to get aMemory
orFunction
we'd be back needing two allocations, one for the store and one for the object itself.To solve both problems we could introduce new memory/function types which are
ref struct
s (e.g.RefStructMemory
) which are returned by new methods (e.g.Caller.GetRefMemory
). Since these would be ref structs they could contain theStoreContext
directly. Again this also models the intended lifetime better.Obviously this is a lot of duplicated code, but I think we could reduce that by putting all of the actual work inside the
RefStructXXX
types and the current class types would become wrappers which internally create the relevantRefStructXXX
, call into it and immediately discard it.Thoughts?
I realise this is a huge amount of churn, but given that it improves the handling of lifetimes as well as performance I think it's probably worth it.
The text was updated successfully, but these errors were encountered: