microvium
microvium copied to clipboard
Shared microvium instances with private heaps
We'd like to be able to have a single instance of the microvium code, but allow different compartments to use it with different heaps. This requires our malloc / free callbacks to have some way of identifying which mvm_VM
they are being called on behalf of (and, ideally, getting some state from it). My ideal interface would make it possible to add a header to the mvm_VM
structure and have the mvm_VM
be passed to the alloc and free callbacks.
Somewhat related: it would be nice if the functions in microvium.h
that are implemented in microvium.c
had a MICROVIUM_EXPORT
macro in front of them (with a default empty definition) that integrators can use to define custom calling conventions, visibility, and so on.
Hi David. Yes, that sounds like something useful. However, mvm_restore
also performs a malloc but before the vm
exists (because it's the malloc for the vm itself). What if the MVM_MALLOC macro instead accepted the context
as an argument rather than the VM itself?
My ideal interface would make it possible to add a header to the mvm_VM structure
I'm not sure what you mean by "header" here. If you're talking about a field, then the context
field may be what you're looking for. User code passes it in to mvm_restore
, and can access it again using mvm_getContext
. It can be anything you like that fits in a pointer.
Somewhat related: it would be nice if the functions in microvium.h that are implemented in microvium.c had a MICROVIUM_EXPORT macro in front of them (with a default empty definition) that integrators can use to define custom calling conventions, visibility, and so on.
Can you give me an example to make it clearer?
I'm not sure what you mean by "header" here. If you're talking about a field, then the context field may be what you're looking for. User code passes it in to mvm_restore, and can access it again using mvm_getContext. It can be anything you like that fits in a pointer.
That would be ideal. If the allocate and deallocate functions were simply passed this context, then this would be very easy for us to support. I believe this could be implemented in a backwards-compatible way by doing something like:
#ifndef MVM_CONTEXT_MALLOC
# define MVM_CONTEXT_MALLOC(ctx, size) MVM_MALLOC(size)
#endif
And then replacing uses of MVM_MALLOC
with MVM_CONTEXT_MALLOC
in the implementaiton.
That way, if someone implements MVM_MALLOC
but not MVM_CONTEXT_MALLOC
, it just calls that. For our use, this would let us put Microvium in a shared code region (read-only, no globals) but still have it allocate memory that can be accounted to each the compartment that instantiates a JavaScript heap (so we could very cheaply have multiple hardware-isolated JavaScript VMs on a tiny device).
Can you give me an example to make it clearer?
#ifndef MICROVIUM_EXPORT
# define MICROVIUM_EXPORT
#endif
...
MICROVIUM_EXPORT mvm_TeError mvm_restore(mvm_VM** result, MVM_LONG_PTR_TYPE snapshotBytecode, size_t bytecodeSize, void* context, mvm_TfResolveImport resolveImport);
We would then #define
this to an attribute that specifies the calling convention that we want. If embedders don't care about this, it doesn't affect them.
Ok, sure. Do you want to take a look at this PR and tell me if it looks right for you?
https://github.com/coder-mike/microvium/pull/52
In particular, I'm not sure if MVM_EXPORT needs to be on both the header declaration and the implementation.
I've tested this and, with the following diff, am now able to run multiple JavaScript VMs, in different compartments, sharing the same code:
diff --git a/dist-c/microvium.h b/dist-c/microvium.h
index 29024d8..1985778 100644
--- a/dist-c/microvium.h
+++ b/dist-c/microvium.h
@@ -202,8 +202,8 @@ MVM_EXPORT void* mvm_getContext(mvm_VM* vm);
MVM_EXPORT void mvm_initializeHandle(mvm_VM* vm, mvm_Handle* handle); // Handle must be released by mvm_releaseHandle
MVM_EXPORT void mvm_cloneHandle(mvm_VM* vm, mvm_Handle* target, const mvm_Handle* source); // Target must be released by mvm_releaseHandle
MVM_EXPORT mvm_TeError mvm_releaseHandle(mvm_VM* vm, mvm_Handle* handle);
-MVM_EXPORT static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; }
-MVM_EXPORT static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; }
+static inline mvm_Value mvm_handleGet(const mvm_Handle* handle) { return handle->_value; }
+static inline void mvm_handleSet(mvm_Handle* handle, mvm_Value value) { handle->_value = value; }
/**
* Roughly like the `typeof` operator in JS, except with distinct values for
I didn't notice when I looked at the PR that the export macro was added to static inline functions (which are not exported).
Yeah, sorry, good point. Thanks for the PR -- I've merged it.
I think this can be closed now - everything seems to be working nicely for us. Thanks!