Skip to content

Commit

Permalink
feat(runtime_capi): add garbage collection methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Wodann committed May 6, 2020
1 parent 06741e3 commit b1979aa
Show file tree
Hide file tree
Showing 9 changed files with 401 additions and 123 deletions.
9 changes: 5 additions & 4 deletions crates/mun_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ mod macros;
mod garbage_collector;
mod marshal;
mod reflection;
mod r#struct;
mod struct_ref;

use failure::Error;
use garbage_collector::{GarbageCollector, UnsafeTypeInfo};
use garbage_collector::GarbageCollector;
use memory::gc::{self, GcRuntime};
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
use rustc_hash::FxHashMap;
Expand All @@ -35,9 +35,10 @@ use std::{

pub use crate::{
assembly::Assembly,
garbage_collector::UnsafeTypeInfo,
marshal::Marshal,
r#struct::StructRef,
reflection::{ArgumentReflection, ReturnTypeReflection},
struct_ref::StructRef,
};
pub use abi::IntoFunctionInfo;

Expand Down Expand Up @@ -289,7 +290,7 @@ impl Runtime {
///
/// We cannot return an `Arc` here, because the lifetime of data contained in `GarbageCollector`
/// is dependent on the `Runtime`.
pub(crate) fn gc(&self) -> &GarbageCollector {
pub fn gc(&self) -> &dyn GcRuntime<UnsafeTypeInfo> {
self.gc.as_ref()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ impl RawStruct {
}

/// Type-agnostic wrapper for interoperability with a Mun struct.
/// TODO: Handle destruction of `struct(value)`
#[derive(Clone)]
pub struct StructRef {
handle: GcRootPtr,
Expand Down Expand Up @@ -263,12 +262,11 @@ impl Marshal<StructRef> for RawStruct {
let src = ptr.cast::<u8>().as_ptr() as *const _;
let dest = unsafe { gc_handle.deref_mut::<u8>() };
let size = type_info.size_in_bytes();
unsafe { ptr::copy_nonoverlapping(src, dest, size as usize) };
unsafe { ptr::copy_nonoverlapping(src, dest, size) };

gc_handle
} else {
// For a gc struct, `ptr` points to a `GcPtr`.

unsafe { *ptr.cast::<GcPtr>().as_ptr() }
};

Expand Down
1 change: 1 addition & 0 deletions crates/mun_runtime_capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ rand = "0.7.2"

[dev-dependencies]
compiler = { path="../mun_compiler", package = "mun_compiler" }
paste = "0.1"
tempfile = "3"
3 changes: 2 additions & 1 deletion crates/mun_runtime_capi/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ line_length = 100
tab_width = 4

[export]
include = ["StructInfo"]
prefix = "Mun"

[parse]
parse_deps = true
include = ["mun_abi"]
include = ["mun_abi", "mun_memory", "mun_runtime"]
177 changes: 177 additions & 0 deletions crates/mun_runtime_capi/src/gc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//! Exposes Mun garbage collection.
use crate::{ErrorHandle, RuntimeHandle, HUB};
use failure::err_msg;
use runtime::Runtime;

pub use memory::gc::GcPtr;
pub use runtime::UnsafeTypeInfo;

/// Allocates an object in the runtime of the given `type_info`. If successful, `obj` is set,
/// otherwise a non-zero error handle is returned.
///
/// If a non-zero error handle is returned, it must be manually destructed using
/// [`mun_error_destroy`].
///
/// # Safety
///
/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn mun_gc_alloc(
handle: RuntimeHandle,
type_info: UnsafeTypeInfo,
obj: *mut GcPtr,
) -> ErrorHandle {
let runtime = match (handle.0 as *mut Runtime).as_ref() {
Some(runtime) => runtime,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'runtime' is null pointer."))
}
};

let obj = match obj.as_mut() {
Some(obj) => obj,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'obj' is null pointer."))
}
};

*obj = runtime.gc().alloc(type_info);
ErrorHandle::default()
}

/// Retrieves the `type_info` for the specified `obj` from the runtime. If successful, `type_info`
/// is set, otherwise a non-zero error handle is returned.
///
/// If a non-zero error handle is returned, it must be manually destructed using
/// [`mun_error_destroy`].
///
/// # Safety
///
/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn mun_gc_ptr_type(
handle: RuntimeHandle,
obj: GcPtr,
type_info: *mut UnsafeTypeInfo,
) -> ErrorHandle {
let runtime = match (handle.0 as *mut Runtime).as_ref() {
Some(runtime) => runtime,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'runtime' is null pointer."))
}
};

let type_info = match type_info.as_mut() {
Some(type_info) => type_info,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'type_info' is null pointer."))
}
};

*type_info = runtime.gc().ptr_type(obj);
ErrorHandle::default()
}

/// Roots the specified `obj`, which keeps it and objects it references alive. Objects marked as
/// root, must call `mun_gc_unroot` before they can be collected. An object can be rooted multiple
/// times, but you must make sure to call `mun_gc_unroot` an equal number of times before the
/// object can be collected. If successful, `obj` has been rooted, otherwise a non-zero error handle
/// is returned.
///
/// If a non-zero error handle is returned, it must be manually destructed using
/// [`mun_error_destroy`].
///
/// # Safety
///
/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn mun_gc_root(handle: RuntimeHandle, obj: GcPtr) -> ErrorHandle {
let runtime = match (handle.0 as *mut Runtime).as_ref() {
Some(runtime) => runtime,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'runtime' is null pointer."))
}
};

runtime.gc().root(obj);
ErrorHandle::default()
}

/// Unroots the specified `obj`, potentially allowing it and objects it references to be
/// collected. An object can be rooted multiple times, so you must make sure to call `mun_gc_unroot`
/// the same number of times as `mun_gc_root` was called before the object can be collected. If
/// successful, `obj` has been unrooted, otherwise a non-zero error handle is returned.
///
/// If a non-zero error handle is returned, it must be manually destructed using
/// [`mun_error_destroy`].
///
/// # Safety
///
/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn mun_gc_unroot(handle: RuntimeHandle, obj: GcPtr) -> ErrorHandle {
let runtime = match (handle.0 as *mut Runtime).as_ref() {
Some(runtime) => runtime,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'runtime' is null pointer."))
}
};

runtime.gc().unroot(obj);
ErrorHandle::default()
}

/// Collects all memory that is no longer referenced by rooted objects. If successful, `reclaimed`
/// is set, otherwise a non-zero error handle is returned. If `reclaimed` is `true`, memory was
/// reclaimed, otherwise nothing happend. This behavior will likely change in the future.
///
/// If a non-zero error handle is returned, it must be manually destructed using
/// [`mun_error_destroy`].
///
/// # Safety
///
/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn mun_gc_collect(
handle: RuntimeHandle,
reclaimed: *mut bool,
) -> ErrorHandle {
let runtime = match (handle.0 as *mut Runtime).as_ref() {
Some(runtime) => runtime,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'runtime' is null pointer."))
}
};

let reclaimed = match reclaimed.as_mut() {
Some(reclaimed) => reclaimed,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'reclaimed' is null pointer."))
}
};

*reclaimed = runtime.gc_collect();
ErrorHandle::default()
}
53 changes: 5 additions & 48 deletions crates/mun_runtime_capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![warn(missing_docs)]

pub mod error;
pub mod gc;
pub mod hub;

#[cfg(test)]
Expand All @@ -15,12 +16,13 @@ use std::{os::raw::c_char, time::Duration};

use crate::error::ErrorHandle;
use crate::hub::HUB;
use abi::{FunctionInfo, StructInfo, TypeInfo};
use failure::err_msg;
use runtime::{Runtime, RuntimeOptions};

pub(crate) type Token = usize;

pub use memory::gc::GcPtr;

/// A type to uniquely index typed collections.
pub trait TypedHandle {
/// Constructs a new `TypedHandle`.
Expand Down Expand Up @@ -115,7 +117,7 @@ pub unsafe extern "C" fn mun_runtime_get_function_info(
handle: RuntimeHandle,
fn_name: *const c_char,
has_fn_info: *mut bool,
fn_info: *mut FunctionInfo,
fn_info: *mut abi::FunctionInfo,
) -> ErrorHandle {
let runtime = match (handle.0 as *mut Runtime).as_ref() {
Some(runtime) => runtime,
Expand Down Expand Up @@ -207,51 +209,6 @@ pub unsafe extern "C" fn mun_runtime_update(
ErrorHandle::default()
}

/// Retrieves the [`StructInfo`] corresponding to `type_info`, if the type is a struct. If
/// successful, `struct_info` is set, otherwise a non-zero error handle is returned.
///
/// If a non-zero error handle is returned, it must be manually destructed using
/// [`mun_error_destroy`].
///
/// # Safety
///
/// This function receives raw pointers as parameters. If any of the arguments is a null pointer,
/// an error will be returned. Passing pointers to invalid data, will lead to undefined behavior.
#[no_mangle]
pub unsafe extern "C" fn mun_type_info_as_struct(
type_info: *const TypeInfo,
struct_info: *mut StructInfo,
) -> ErrorHandle {
let type_info = match type_info.as_ref() {
Some(info) => info,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'type_info' is null pointer."))
}
};

let struct_info = match struct_info.as_mut() {
Some(info) => info,
None => {
return HUB
.errors
.register(err_msg("Invalid argument: 'struct_info' is null pointer."))
}
};

match type_info.as_struct() {
Some(info) => *struct_info = info.clone(),
None => {
return HUB
.errors
.register(err_msg(format!("`{}` is not a struct.", type_info.name())))
}
}

ErrorHandle::default()
}

/// Deallocates a string that was allocated by the runtime.
///
/// # Safety
Expand All @@ -260,7 +217,7 @@ pub unsafe extern "C" fn mun_type_info_as_struct(
/// its content will be deallocated. Passing pointers to invalid data or memory allocated by other
/// processes, will lead to undefined behavior.
#[no_mangle]
pub unsafe fn mun_destroy_string(string: *const c_char) {
pub unsafe extern "C" fn mun_destroy_string(string: *const c_char) {
if !string.is_null() {
// Destroy the string
let _string = CString::from_raw(string as *mut _);
Expand Down
Loading

0 comments on commit b1979aa

Please sign in to comment.