Mu Client Interface as C Binding
Created by: wks
The current API is expressed in a language-neutral form, and it is the implementation that decides how to implement such an interface. Programmers still need to resort to implementation-specific interfaces to actually use a particular Mu implementation.
Since C is so widely used as a system programming language, the Mu client interface (a.k.a the API) should be expressed as data types and function calls in the C programming language. If the client is not in C, it usually still has a C FFI.
The API in C
Resources in Mu are exposed in opaque types which have reference semantics: they can be copied and still refers to the same resource.
-
mu_micro_vm_t
: a reference to a Mu micro VM instance. -
mu_client_agent_t
: a reference to a client agent. -
mu_handle_t
: a handle to a value in the Mu type system exposed to the client.
Messages are C functions. Like JNI, they are contained in a struct: typedef struct mu_api_msgs {...} mu_api_msgs_t
. In this way, the client in C does not need to link against any libraries when compiling. The reason is, for a Mu micro VM implemented in a higher-level language (like the reference implementation in Scala), the binding of the callable C function is generated very late, later than even the loading time, and has no access to the native loader.
For example, assume there is a mu_api_msgs_t* msgs
defined:
mu_client_agent_t ca = ...
char buf[999999];
int sz;
// load file into buf
msgs->load_bundle(ca, buf, sz); // Load a bundle
// Putting C values into Mu
mu_handle_t h1 = msgs->put_schar(ca, 127);
mu_handle_t h2 = msgs->put_sshort(ca, 32767);
mu_handle_t h3 = msgs->put_sint(ca, 42);
mu_handle_t h4 = msgs->put_slong(ca, 42);
mu_handle_t h5 = msgs->put_slonglong(ca, 999999999999999);
// Converting Mu values to C
int v3 = msgs->to_sint(ca, h3);
unsigned long v4 = msgs->to_ulong(ca, h4); // just treat the int as unsigned
Mu-level flags are C preprocessor macros. They have type int.
msgs->store(SEQ_CST, hLoc, hNewVal); // SEQ_CST is a macro
Callbacks, including the trap handler and undefined function handler, have defined signatures:
typedef mu_trap_return_status_t (*mu_trap_handler_t)(
mu_client_agent_t ca,
mu_handle_t stack,
mu_handle_t thread,
int watchpoint_id,
mu_handle_t &new_stack,
mu_handle_t &data_passed,
mu_handle_t &new_exception,
mu_api_msgs *msgs,
void *user_data);
typedef void (*mu_undefined_function_handler_t)(
mu_micro_vm_t microvm,
int funciton_id,
mu_api_msgs *msgs,
void *user_data);
These functions are registered via the msgs->register_trap_handler
and msgs->register_undefined_function_handler
API messages. In their parameters, the user_data
is an arbitrary pointer provided by the client in an implementation-specific manner (see below).
Implementation-defined behaviours
Some aspects of the C binding are implementation-specified. They include:
- How to create a Mu micro VM? Options are:
- The C executable creates the Mu instance.
- Mu loads the C dynamic library.
- Mu starts separately and C connects to the existing instance in the same process.
- C connects to a Mu instance in a different process, or a different machine.
- Options in creating Mu instances. Options are:
- Heap size. Giving a heap size means the Client determines the heap size rather than Mu automatically decide its own storage.
- Global data space size. Setting this value means the global data may have their own storage. Actual implementation could use the heap space, too.
- Stack size. Similarly, this is too implementation-specific.
- What happens during initialisation?
- Mu calls a C function to initialise the client, and the client provides a
void*
to Mu for the client's own context. (note: in this case, it is Mu loading C rather than C creating Mu.) - C creates a Mu instance, and sets its
void*
user data in a proprietary API message.
- Mu calls a C function to initialise the client, and the client provides a
Open questions
- Should we allow each Mu implementation have its own "namespace"? The opaque types (
mu_micro_vm_t
and so on) are opaque, but different implementations may have different representations. The current C binding design forbids one C program working with more than one Mu implementations (though it is okay to work with more than one instances of the same implementation).- JNI does not solve this problem, either.