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