GitLab will continue to be upgraded from 11.4.5-ce.0 on November 25th 2019 at 4.00pm (AEDT) to 5.00pm (AEDT) due to Critical Security Patch Availability. During the update, GitLab and Mattermost services will not be available.

Commit 8d38c30d authored by Kunshan Wang's avatar Kunshan Wang

API: Creation and destruction.

Creaation and destruction of MuVM, MuCtx and MuIRBuilder. Now the
`mvm->new_context`, `ctx->new_ir_builder`, `ctx->close_context` and
`b->abort` works. MuVM can also be created via a top-level "C"-style
exported function `mu_fastimpl_new()` so that in theory, it can be used
in C programs.
parent f92bb162
......@@ -18,11 +18,13 @@ pub struct MuVM {
// Stub
}
pub struct MuCtx {
pub struct MuCtx<'v> {
mvm: &'v mut MuVM,
// Stub
}
pub struct MuIRBuilder {
pub struct MuIRBuilder<'c> {
ctx: &'c mut MuCtx<'c>,
// Stub
}
......@@ -46,7 +48,7 @@ impl MuVM {
}
impl MuCtx {
impl<'v> MuCtx<'v> {
pub fn id_of(&mut self, name: MuName) -> MuID {
panic!("Not implemented")
}
......@@ -405,7 +407,7 @@ impl MuCtx {
}
impl MuIRBuilder {
impl<'c> MuIRBuilder<'c> {
pub fn load(&mut self) {
panic!("Not implemented")
}
......
......@@ -46,18 +46,18 @@ fn from_MuVM_ptr(ptr: *mut CMuVM) -> *mut MuVM {
}
#[inline(always)]
fn from_MuCtx_ptr(ptr: *mut CMuCtx) -> *mut MuCtx {
fn from_MuCtx_ptr<'v>(ptr: *mut CMuCtx) -> *mut MuCtx<'v> {
debug_assert!(!ptr.is_null());
unsafe {
(*ptr).header as *mut MuCtx
(*ptr).header as *mut MuCtx<'v>
}
}
#[inline(always)]
fn from_MuIRBuilder_ptr(ptr: *mut CMuIRBuilder) -> *mut MuIRBuilder {
fn from_MuIRBuilder_ptr<'c>(ptr: *mut CMuIRBuilder) -> *mut MuIRBuilder<'c> {
debug_assert!(!ptr.is_null());
unsafe {
(*ptr).header as *mut MuIRBuilder
(*ptr).header as *mut MuIRBuilder<'c>
}
}
......
......@@ -18,29 +18,126 @@ use std::ffi::CString;
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Mutex;
use super::super::vm::VM;
use super::api_c::*;
use super::api_bridge::*;
use super::deps::*;
/**
* Create a micro VM instance, and expose it as a C-visible `*mut CMuVM` pointer.
*
* NOTE: When used as an API (such as in tests), please use `mu::vm::api::mu_fastimpl_new` instead.
*
* This method is not part of the API defined by the Mu spec. It is used **when the client starts
* the process and creates the micor VM**. For example, it is used if the client wants to build
* boot images, or if the client implements most of its parts in C and onlu uses the micro VM as
* the JIT compiler.
*
* The boot image itself should use `VM::resume_vm` to restore the saved the micro VM. There is no
* need in the boot image itself to expose the `MuVM` structure to the trap handler. Trap handlers
* only see `MuCtx`, and it is enough for most of the works.
*/
#[no_mangle]
pub extern fn mu_fastimpl_new() -> *mut CMuVM {
info!("Creating Mu micro VM fast implementation instance...");
let mvm = Box::new(MuVM::new());
let mvm_ptr = Box::into_raw(mvm);
debug!("The MuVM instance address: {:?}", mvm_ptr);
let c_mvm = make_new_MuVM(mvm_ptr as *mut c_void);
debug!("The C-visible CMuVM struct address: {:?}", c_mvm);
c_mvm
}
pub struct MuVM {
// The actual VM
vm: VM,
// Cache C strings. The C client expects `char*` from `name_of`. We assume the client won't
// call `name_of` very often, so that we don't need to initialise this hashmap on startup.
name_cache: Mutex<HashMap<MuID, CString>>,
}
pub struct MuCtx {
pub struct MuCtx<'v> {
// ref to the MuVM struct.
mvm: &'v mut MuVM,
// Point to the C-visible CMuCtx so that `close_context` can deallocate itself.
c_struct: *mut CMuCtx,
}
pub struct MuIRBuilder {
pub struct MuIRBuilder<'c> {
// ref to the MuCtx struct.
ctx: &'c mut MuCtx<'c>,
// Point to the C-visible CMuIRBuilder so that `load` and `abort` can deallocate itself.
c_struct: *mut CMuIRBuilder,
}
/**
* Implement the methods of MuVM. Most methods implement the C-level methods, and others are
* rust-level helpers. Most methods are forwarded to the underlying `VM.*` methods.
*/
impl MuVM {
/**
* Create a new micro VM instance from scratch.
*/
pub fn new() -> MuVM {
MuVM {
vm: VM::new(),
// Cache C strings. The C client expects `char*` from `name_of`. We assume the client
// won't call `name_of` very often, so that we don't need to initialise this hashmap on
// startup.
//
// RwLock won't work because Rust will not let me release the lock after reading
// because other threads will remove that element from the cache, even though I only
// monotonically add elements into the `name_cache`. I can't upgrade the lock from read
// lock to write lock, otherwise it will deadlock.
name_cache: Mutex::new(HashMap::new()),
}
}
pub fn new_context(&mut self) -> *mut CMuCtx {
panic!("Not implemented")
info!("Creating MuCtx...");
let ctx = Box::new(MuCtx {
mvm: self,
c_struct: ptr::null_mut(),
});
let ctx_ptr = Box::into_raw(ctx);
debug!("The MuCtx address: {:?}", ctx_ptr);
let cctx = make_new_MuCtx(ctx_ptr as *mut c_void);
debug!("The C-visible CMuCtx struct address: {:?}", cctx);
unsafe{ (*ctx_ptr).c_struct = cctx; }
cctx
}
pub fn id_of(&mut self, name: MuName) -> MuID {
panic!("Not implemented")
self.vm.id_of_by_refstring(&name)
}
pub fn name_of(&mut self, id: MuID) -> CMuCString {
panic!("Not implemented")
let mut map = self.name_cache.lock().unwrap();
let cname = map.entry(id).or_insert_with(|| {
let rustname = self.vm.name_of(id);
CString::new(rustname).unwrap()
});
cname.as_ptr()
}
pub fn set_trap_handler(&mut self, trap_handler: CMuTrapHandler, userdata: CMuCPtr) {
......@@ -49,25 +146,41 @@ impl MuVM {
}
impl MuCtx {
impl<'v> MuCtx<'v> {
#[inline(always)]
fn get_mvm(&mut self) -> &mut MuVM {
self.mvm
}
pub fn id_of(&mut self, name: MuName) -> MuID {
panic!("Not implemented")
self.get_mvm().id_of(name)
}
pub fn name_of(&mut self, id: MuID) -> CMuCString {
panic!("Not implemented")
self.get_mvm().name_of(id)
}
fn deallocate(&mut self) {
let c_struct = self.c_struct;
let ctx_ptr = self as *mut MuCtx;
debug!("Deallocating MuCtx {:?} and CMuCtx {:?}...", ctx_ptr, c_struct);
unsafe {
Box::from_raw(c_struct);
Box::from_raw(ctx_ptr);
}
}
pub fn close_context(&mut self) {
panic!("Not implemented")
info!("Closing MuCtx...");
self.deallocate();
}
pub fn load_bundle(&mut self, buf: &[c_char]) {
panic!("Not implemented")
panic!("The fast implementation does not support the text form.")
}
pub fn load_hail(&mut self, buf: &[c_char]) {
panic!("Not implemented")
panic!("The fast implementation does not support the text form.")
}
pub fn handle_from_sint8(&mut self, num: i8, len: c_int) -> *const APIMuValue {
......@@ -398,8 +511,25 @@ impl MuCtx {
panic!("Not implemented")
}
pub fn new_ir_builder(&mut self) -> *mut CMuIRBuilder {
panic!("Not implemented")
pub fn new_ir_builder(&'v mut self) -> *mut CMuIRBuilder {
info!("Creating MuIRBuilder...");
let b: Box<MuIRBuilder<'v>> = Box::new(MuIRBuilder {
ctx: self,
c_struct: ptr::null_mut(),
});
let b_ptr = Box::into_raw(b);
debug!("The MuIRBuilder address: {:?}", b_ptr);
let cb = make_new_MuIRBuilder(b_ptr as *mut c_void);
debug!("The C-visible CMuIRBuilder struct address: {:?}", cb);
unsafe{ (*b_ptr).c_struct = cb; }
cb
}
pub fn make_boot_image(&mut self, whitelist: Vec<MuID>, primordial_func: Option<&APIMuValue>, primordial_stack: Option<&APIMuValue>, primordial_threadlocal: Option<&APIMuValue>, sym_fields: Vec<&APIMuValue>, sym_strings: Vec<String>, reloc_fields: Vec<&APIMuValue>, reloc_strings: Vec<String>, output_file: String) {
......@@ -408,16 +538,49 @@ impl MuCtx {
}
impl MuIRBuilder {
impl<'c> MuIRBuilder<'c> {
#[inline(always)]
fn get_ctx(&'c mut self) -> &mut MuCtx {
self.ctx
}
#[inline(always)]
fn get_mvm(&'c mut self) -> &mut MuVM {
self.get_ctx().get_mvm()
}
#[inline(always)]
fn get_vm(&'c mut self) -> &mut VM {
&mut self.get_mvm().vm
}
#[inline(always)]
fn next_id(&'c mut self) -> MuID {
self.get_vm().next_id()
}
fn deallocate(&mut self) {
let c_struct = self.c_struct;
let b_ptr = self as *mut MuIRBuilder;
debug!("Deallocating MuIRBuilder {:?} and CMuIRBuilder {:?}...", b_ptr, c_struct);
unsafe {
Box::from_raw(c_struct);
Box::from_raw(b_ptr);
}
}
pub fn load(&mut self) {
panic!("Not implemented")
panic!("Please implement bundle loading before deallocating itself.");
self.deallocate();
}
pub fn abort(&mut self) {
panic!("Not implemented")
info!("Aborting boot image building...");
self.deallocate();
}
pub fn gen_sym(&mut self, name: Option<String>) -> MuID {
pub fn gen_sym(&'c mut self, name: Option<String>) -> MuID {
let my_id = self.next_id();
panic!("Not implemented")
}
......
......@@ -108,13 +108,14 @@ pub struct MuVM {
trap_handler_user_data: Option<CMuCPtr>,
}
pub struct MuCtx {
mvm: *mut MuVM,
pub struct MuCtx<'v> {
mvm: &'v mut MuVM,
c_struct: *mut CMuCtx,
handles: HashSet<*const APIMuValue>,
}
pub struct MuIRBuilder {
pub struct MuIRBuilder<'c> {
ctx: &'c mut MuCtx<'c>,
// Stub
}
......@@ -142,13 +143,6 @@ impl MuVM {
}
fn dealloc_context(&mut self, ctx: &mut MuCtx) {
let c_struct = ctx.c_struct;
let ctx_ptr = ctx as *mut MuCtx;
println!("Deallocating MuCtx {:?} and CMuCtx {:?}...", ctx_ptr, c_struct);
unsafe {
Box::from_raw(c_struct);
Box::from_raw(ctx_ptr);
}
}
pub fn id_of(&mut self, name: MuName) -> MuID {
......@@ -227,9 +221,9 @@ impl MuVM {
}
impl MuCtx {
fn get_mvm<'a>(&mut self) -> &'a mut MuVM {
unsafe { &mut * self.mvm }
impl<'v> MuCtx<'v> {
fn get_mvm(&mut self) -> &mut MuVM {
self.mvm
}
pub fn id_of(&mut self, name: MuName) -> MuID {
......@@ -246,7 +240,13 @@ impl MuCtx {
for &ptr in self.handles.iter() {
MuCtx::dealloc_handle(ptr);
}
self.get_mvm().dealloc_context(self)
let c_struct = self.c_struct;
let ctx_ptr = self as *mut MuCtx;
println!("Deallocating MuCtx {:?} and CMuCtx {:?}...", ctx_ptr, c_struct);
unsafe {
Box::from_raw(c_struct);
Box::from_raw(ctx_ptr);
}
}
pub fn load_bundle(&mut self, buf: &[c_char]) {
......@@ -617,7 +617,7 @@ impl MuCtx {
}
impl MuIRBuilder {
impl<'c> MuIRBuilder<'c> {
pub fn load(&mut self) {
panic!("Not implemented")
}
......
mod api_c;
mod api_bridge;
mod api_impl;
pub mod api_c; // This is pub because `api_c` can be used directly. It is just an interface.
mod api_bridge; // This is mostly auto-generatd code, and should not be used externally.
mod api_impl; // Mostly private.
pub use self::api_impl::mu_fastimpl_new;
mod deps {
pub use ast::ir::WPID;
......
......@@ -384,6 +384,12 @@ def visit_method(st, meth) -> Tuple[str, str, str, str]:
return field_def, bridge, filler_stmt, stub
_lifetime_params = {
"MuVM": "",
"MuCtx": "<'v>",
"MuIRBuilder": "<'c>",
}
def visit_struct(st) -> Tuple[str, List[str], str, str]:
name = st["name"]
methods = st["methods"]
......@@ -429,8 +435,10 @@ pub fn make_new_{name}(header: *mut c_void) -> *mut {rust_name} {{
stubs_joined = "\n".join(stubs)
lifetime_params = _lifetime_params[name]
stub_impl = """\
impl {name} {{
impl{lifetime_params} {name}{lifetime_params} {{
{stubs_joined}
}}
""".format(**locals())
......
......@@ -336,7 +336,16 @@ impl <'a> VM {
}
ret.is_running.store(false, Ordering::SeqCst);
ret.next_id.store(USER_ID_START, Ordering::SeqCst);
// Does not need SeqCst.
//
// If VM creates Mu threads and Mu threads calls traps, the trap handler still "happens
// after" the creation of the VM itself. Rust does not have a proper memory model, but this
// is how C++ works.
//
// If the client needs to create client-level threads, however, the client should properly
// synchronise at the time of inter-thread communication, rather than creation of the VM.
ret.next_id.store(USER_ID_START, Ordering::Relaxed);
let options = VMOptions::default();
gc::gc_init(options.immix_size, options.lo_size, options.n_gcthreads);
......@@ -356,7 +365,10 @@ impl <'a> VM {
}
pub fn next_id(&self) -> MuID {
self.next_id.fetch_add(1, Ordering::SeqCst)
// This only needs to be atomic, and does not need to be a synchronisation operation. The
// only requirement for IDs is that all IDs obtained from `next_id()` are different. So
// `Ordering::Relaxed` is sufficient.
self.next_id.fetch_add(1, Ordering::Relaxed)
}
pub fn run_vm(&self) {
......@@ -378,9 +390,13 @@ impl <'a> VM {
map2.insert(name, id);
}
pub fn id_of(&self, name: &str) -> MuID {
pub fn id_of_by_refstring(&self, name: &String) -> MuID {
let map = self.name_id_map.read().unwrap();
*map.get(&name.to_string()).unwrap()
*map.get(name).unwrap()
}
pub fn id_of(&self, name: &str) -> MuID {
self.id_of_by_refstring(&name.to_string())
}
pub fn name_of(&self, id: MuID) -> MuName {
......@@ -575,4 +591,4 @@ impl <'a> VM {
unimplemented!()
}
}
\ No newline at end of file
}
pub mod test_ir;
mod test_types;
mod test_builder_api;
pub mod test_builder_api;
......@@ -2,6 +2,9 @@
#![allow(dead_code)]
extern crate mu;
extern crate log;
extern crate simple_logger;
use self::mu::ast::types::*;
use self::mu::ast::ir::*;
use self::mu::ast::inst::*;
......@@ -24,3 +27,25 @@ fn builder_factorial() {
// let ctx = (mvm_ref.new_context)(mvm);
// let ctx_ref = unsafe {ctx.as_mut()}.unwrap();
}
#[test]
#[allow(unused_variables)]
fn test_startup_shutdown() {
unsafe {
simple_logger::init_with_level(log::LogLevel::Trace).ok();
info!("Starting micro VM...");
let mvm = mu_fastimpl_new();
let ctx = ((*mvm).new_context)(mvm);
let b = ((*ctx).new_ir_builder)(ctx);
((*b).abort)(b);
((*ctx).close_context)(ctx);
info!("Finished.");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment