diff --git a/Cargo.toml b/Cargo.toml index 7b6df82..d955671 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ocaml-interop" -version = "0.8.4" # remember to update html_root_url +version = "0.9.0" # remember to update html_root_url authors = ["Bruno Deferrari "] license = "MIT" description = "Utilities for Rust and OCaml interoperability" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml new file mode 100644 index 0000000..c16bdc4 --- /dev/null +++ b/runtime/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "ocaml-interop-runtime" +version = "0.9.0" # remember to update html_root_url +authors = ["Bruno Deferrari "] +license = "MIT" +description = "Runtime utilities for Rust and OCaml interoperability" +homepage = "http://github.com/tezedge/ocaml-interop" +repository = "http://github.com/tezedge/ocaml-interop" +keywords = ["ocaml", "rust", "ffi", "interop"] +edition = "2018" + +[dependencies] +ocaml-interop = "0.9.0" +ocaml-sys = "^0.20.1" +ocaml-boxroot-sys = "0.2" +static_assertions = "1.1.0" diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs new file mode 100644 index 0000000..dede80e --- /dev/null +++ b/runtime/src/lib.rs @@ -0,0 +1,28 @@ +// Copyright (c) Viable Systems and TezEdge Contributors +// SPDX-License-Identifier: MIT +use std::sync::Once; +use ocaml_sys::{caml_startup}; +use ocaml_interop::OCamlRuntime; + +/// Initializes the OCaml runtime and returns an OCaml runtime handle. +/// +/// Once the handle is dropped, the OCaml runtime will be shutdown. +pub fn init() -> OCamlRuntime { + init_persistent(); + unsafe { ocaml_interop::OCamlRuntime::create() } +} + +/// Initializes the OCaml runtime. +/// +/// After the first invocation, this method does nothing. +pub fn init_persistent() { + static INIT: Once = Once::new(); + + INIT.call_once(|| { + let arg0 = "ocaml\0".as_ptr() as *const ocaml_sys::Char; + let c_args = vec![arg0, core::ptr::null()]; + unsafe { + caml_startup(c_args.as_ptr()); + } + }) +} diff --git a/src/lib.rs b/src/lib.rs index 2c477e7..a0e2e08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -205,9 +205,10 @@ //! result.to_rust::(cr) as usize //! } //! -//! fn entry_point() { +//! /* NB: Requires ocaml_interop_runtime as a dependency */ +//! /*fn entry_point() { //! // IMPORTANT: the OCaml runtime has to be initialized first. -//! let mut cr = OCamlRuntime::init(); +//! let mut cr = ocaml_interop_runtime::init(); //! // `cr` is the OCaml runtime handle, must be passed to any function //! // that interacts with the OCaml runtime. //! let first_n = twice(&mut cr, 5); @@ -220,7 +221,7 @@ //! println!("Bytes2 after: {}", result2); //! // `OCamlRuntime`'s `Drop` implementation will pefrorm the necessary cleanup //! // to shutdown the OCaml runtime. -//! } +//! }*/ //! ``` //! //! ### Calling into Rust from OCaml diff --git a/src/runtime.rs b/src/runtime.rs index 1ea57ac..cde878f 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: MIT use ocaml_boxroot_sys::{boxroot_setup, boxroot_teardown}; -use ocaml_sys::{caml_shutdown, caml_startup}; -use std::{marker::PhantomData, sync::Once}; +use ocaml_sys::{caml_shutdown}; +use std::{marker::PhantomData}; use crate::{memory::OCamlRef, value::OCaml}; @@ -18,30 +18,21 @@ pub struct OCamlRuntime { } impl OCamlRuntime { - /// Initializes the OCaml runtime and returns an OCaml runtime handle. + /// Recover the runtime handle. /// - /// Once the handle is dropped, the OCaml runtime will be shutdown. - pub fn init() -> Self { - Self::init_persistent(); - Self { _private: () } - } - - /// Initializes the OCaml runtime. + /// # Safety /// - /// After the first invocation, this method does nothing. - pub fn init_persistent() { - static INIT: Once = Once::new(); - - INIT.call_once(|| { - let arg0 = "ocaml\0".as_ptr() as *const ocaml_sys::Char; - let c_args = vec![arg0, core::ptr::null()]; - unsafe { - caml_startup(c_args.as_ptr()); - } - }) + /// This function is unsafe because the OCaml runtime handle should be obtained once + /// upon initialization of the OCaml runtime and then passed around. This method exists + /// to allow the capture of the runtime when it is already known to have been + /// initialized. + #[inline(always)] + pub unsafe fn recover_handle() -> &'static mut Self { + static mut RUNTIME: OCamlRuntime = OCamlRuntime { _private: () }; + &mut RUNTIME } - /// Recover the runtime handle. + /// Create a new runtime handle. /// /// This method is used internally, do not use directly in code, only when writing tests. /// @@ -49,11 +40,12 @@ impl OCamlRuntime { /// /// This function is unsafe because the OCaml runtime handle should be obtained once /// upon initialization of the OCaml runtime and then passed around. This method exists - /// only to ease the authoring of tests. + /// to allow the creation of a runtime after `caml_startup` has been called and the + /// runtime is initialized. The OCaml runtime will be shutdown when this value is + /// dropped. #[inline(always)] - pub unsafe fn recover_handle() -> &'static mut Self { - static mut RUNTIME: OCamlRuntime = OCamlRuntime { _private: () }; - &mut RUNTIME + pub unsafe fn create() -> Self { + OCamlRuntime { _private: () } } /// Release the OCaml runtime lock, call `f`, and re-acquire the OCaml runtime lock. diff --git a/testing/rust-caller/Cargo.toml b/testing/rust-caller/Cargo.toml index 50344a6..d4abce9 100644 --- a/testing/rust-caller/Cargo.toml +++ b/testing/rust-caller/Cargo.toml @@ -7,5 +7,8 @@ edition = "2018" [dependencies.ocaml-interop] path = "../.." +[dependencies.ocaml-interop-runtime] +path = "../../runtime" + [dev-dependencies] serial_test = "*" \ No newline at end of file diff --git a/testing/rust-caller/src/lib.rs b/testing/rust-caller/src/lib.rs index 2d144de..06764d7 100644 --- a/testing/rust-caller/src/lib.rs +++ b/testing/rust-caller/src/lib.rs @@ -161,7 +161,7 @@ use serial_test::serial; #[test] #[serial] fn test_twice() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!(twice(&mut cr, 10), 20); } @@ -169,7 +169,7 @@ fn test_twice() { #[test] #[serial] fn test_increment_bytes() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!( increment_bytes(&mut cr, "0000000000000000", 10), @@ -180,7 +180,7 @@ fn test_increment_bytes() { #[test] #[serial] fn test_increment_ints_list() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; let ints = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; let expected = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -190,7 +190,7 @@ fn test_increment_ints_list() { #[test] #[serial] fn test_make_tuple() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!( make_tuple(&mut cr, "fst".to_owned(), 9), @@ -201,7 +201,7 @@ fn test_make_tuple() { #[test] #[serial] fn test_make_some() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!( make_some(&mut cr, "some".to_owned()), @@ -212,7 +212,7 @@ fn test_make_some() { #[test] #[serial] fn test_make_result() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!(make_ok(&mut cr, 10), Ok(10)); assert_eq!( @@ -224,7 +224,7 @@ fn test_make_result() { #[test] #[serial] fn test_frame_management() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!(allocate_alot(&mut cr), true); } @@ -232,7 +232,7 @@ fn test_frame_management() { #[test] #[serial] fn test_record_conversion() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; let record = ocaml::TestRecord { i: 10, @@ -249,7 +249,7 @@ fn test_record_conversion() { #[test] #[serial] fn test_variant_conversion() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!( verify_variant_test(&mut cr, ocaml::Movement::RotateLeft), @@ -268,7 +268,7 @@ fn test_variant_conversion() { #[test] #[serial] fn test_polymorphic_variant_conversion() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; assert_eq!( verify_polymorphic_variant_test(&mut cr, ocaml::PolymorphicEnum::Unit), @@ -287,7 +287,7 @@ fn test_polymorphic_variant_conversion() { #[test] #[serial] fn test_exception_handling_with_message() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let result = std::panic::catch_unwind(move || { let mut cr = unsafe { OCamlRuntime::recover_handle() }; let mcr = &mut cr; @@ -306,7 +306,7 @@ fn test_exception_handling_with_message() { #[test] #[serial] fn test_exception_handling_without_message() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let result = std::panic::catch_unwind(|| { let cr = unsafe { OCamlRuntime::recover_handle() }; ocaml::raises_nonmessage_exception(cr, &OCaml::unit()); @@ -323,7 +323,7 @@ fn test_exception_handling_without_message() { #[test] #[serial] fn test_exception_handling_nonblock_exception() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let result = std::panic::catch_unwind(|| { let cr = unsafe { OCamlRuntime::recover_handle() }; ocaml::raises_nonblock_exception(cr, &OCaml::unit()); @@ -340,7 +340,7 @@ fn test_exception_handling_nonblock_exception() { #[test] #[serial] fn test_dynbox() { - OCamlRuntime::init_persistent(); + ocaml_interop_runtime::init_persistent(); let mut cr = unsafe { OCamlRuntime::recover_handle() }; let mut list = OCaml::nil().root();