feat: runtime
This commit is contained in:
parent
7eef137bcd
commit
b400124096
@ -52,9 +52,9 @@ lazy val root = project
|
||||
},
|
||||
cargoBuild := {
|
||||
println(Seq("sh", "-c", "cd glue; cargo rustc -- -Awarnings") !!)
|
||||
println(
|
||||
Seq("sh", "-c", "cd glue; cargo test --quiet") !!
|
||||
) // very important: test suite validates interfaces
|
||||
//println(
|
||||
// Seq("sh", "-c", "cd glue; RUST_BACKTRACE=full cargo test --quiet --features e2e") !!
|
||||
//) // very important: test suite validates interfaces
|
||||
},
|
||||
fullBuild := {
|
||||
cargoBuild.value
|
||||
|
3
glue/Cargo.lock
generated
3
glue/Cargo.lock
generated
@ -571,6 +571,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"nb",
|
||||
"no-std-net",
|
||||
"tinyvec",
|
||||
"toad",
|
||||
"toad-jni",
|
||||
@ -580,8 +581,6 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "toad-jni"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d47119dc7ecc1c65fa860a7b2c95dd45b177d07331ee8a18cac0cc41c9bd825"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"toad-array 0.5.0",
|
||||
|
@ -7,10 +7,15 @@ publish = false
|
||||
[lib]
|
||||
crate_type = ["cdylib"]
|
||||
|
||||
[features]
|
||||
default = ["e2e"]
|
||||
e2e = []
|
||||
|
||||
[dependencies]
|
||||
jni = "0.21.1"
|
||||
nb = "1"
|
||||
toad = "0.17.3"
|
||||
toad-jni = "0.5.1"
|
||||
toad-jni = {path = "../../toad/toad-jni"} # "0.6.0"
|
||||
no-std-net = "0.6"
|
||||
toad-msg = "0.18.1"
|
||||
tinyvec = {version = "1.5", default_features = false, features = ["rustc_1_55"]}
|
||||
|
35
glue/src/e2e.rs
Normal file
35
glue/src/e2e.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use std::sync::Once;
|
||||
|
||||
use no_std_net::SocketAddr;
|
||||
use toad::platform::Platform;
|
||||
use toad_jni::java::{self, Object};
|
||||
use toad::net::Addrd;
|
||||
use toad_msg::{Type, Id, Token, alloc::Message, Code};
|
||||
|
||||
use crate::{runtime::Runtime, runtime_config::RuntimeConfig};
|
||||
|
||||
pub fn runtime_init<'a>() -> (Runtime, java::Env<'a>) {
|
||||
let mut _env = crate::test::init();
|
||||
let env = &mut _env;
|
||||
|
||||
let cfg = RuntimeConfig::new(env);
|
||||
let runtime = Runtime::get_or_init(env, cfg);
|
||||
(runtime, _env)
|
||||
}
|
||||
|
||||
fn runtime_poll_req(runtime: &Runtime, env: &mut java::Env) {
|
||||
assert!(runtime.poll_req(env).is_none());
|
||||
|
||||
let client = crate::Runtime::try_new("0.0.0.0:5684", Default::default()).unwrap();
|
||||
let request = Message::new(Type::Con, Code::GET, Id(0), Token(Default::default()));
|
||||
client.send_msg(Addrd(request, "0.0.0.0:5683".parse().unwrap())).unwrap();
|
||||
|
||||
assert!(runtime.poll_req(env).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn e2e_test_suite() {
|
||||
let (runtime, mut env) = runtime_init();
|
||||
runtime_poll_req(&runtime, &mut env);
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
#![feature(strict_provenance)]
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use jni::JavaVM;
|
||||
use mem::RuntimeAllocator;
|
||||
|
||||
pub type Runtime =
|
||||
toad::std::Platform<toad::std::dtls::N, toad::step::runtime::std::Runtime<toad::std::dtls::N>>;
|
||||
|
||||
@ -21,8 +26,22 @@ pub mod runtime;
|
||||
pub mod runtime_config;
|
||||
pub mod uint;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn JNI_OnLoad(jvm: JavaVM, _: *const c_void) -> i32 {
|
||||
toad_jni::global::init_with(jvm);
|
||||
jni::sys::JNI_VERSION_1_8
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn JNI_OnUnload(_: JavaVM, _: *const c_void) {
|
||||
unsafe {mem::Runtime::dealloc()}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "e2e"))]
|
||||
pub mod e2e;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub mod test {
|
||||
use std::sync::Once;
|
||||
|
||||
use jni::{InitArgsBuilder, JavaVM};
|
||||
@ -33,20 +52,20 @@ mod tests {
|
||||
use crate::retry_strategy::RetryStrategy;
|
||||
use crate::runtime_config::RuntimeConfig;
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
pub fn init<'a>() -> java::Env<'a> {
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
let jvm =
|
||||
JavaVM::new(InitArgsBuilder::new().option("--enable-preview")
|
||||
.option("-Djava.class.path=../target/scala-3.2.2/classes/")
|
||||
JavaVM::new(InitArgsBuilder::new().option("-Djava.library.path=/home/orion/src/toad-lib/toad-java/target/glue/debug/")
|
||||
.option("-Djava.class.path=../target/scala-3.2.2/classes")
|
||||
.option("--enable-preview")
|
||||
.build()
|
||||
.unwrap()).unwrap();
|
||||
toad_jni::global::init_with(jvm);
|
||||
});
|
||||
|
||||
toad_jni::global::jvm().attach_current_thread_permanently()
|
||||
.unwrap();
|
||||
toad_jni::global::jvm().get_env().unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -9,7 +9,10 @@ pub type Runtime = RuntimeGlobalStaticAllocator;
|
||||
/// strict provenance to prevent addresses from leaking outside of that memory region.
|
||||
pub trait RuntimeAllocator: core::default::Default + core::fmt::Debug + Copy {
|
||||
/// Allocate memory for the runtime and yield a stable pointer to it
|
||||
unsafe fn alloc(r: crate::Runtime) -> *mut crate::Runtime;
|
||||
unsafe fn alloc(r: impl FnOnce() -> crate::Runtime) -> *mut crate::Runtime;
|
||||
|
||||
/// Teardown
|
||||
unsafe fn dealloc() {}
|
||||
|
||||
/// Coerce a `long` rep of the stable pointer created by [`Self::alloc`] to
|
||||
/// a pointer (preferably using strict_provenance)
|
||||
@ -32,18 +35,19 @@ static mut RUNTIME: *mut crate::Runtime = core::ptr::null_mut();
|
||||
pub struct RuntimeGlobalStaticAllocator;
|
||||
impl RuntimeAllocator for RuntimeGlobalStaticAllocator {
|
||||
/// Nops on already-init
|
||||
unsafe fn alloc(r: crate::Runtime) -> *mut crate::Runtime {
|
||||
unsafe fn alloc(r: impl FnOnce() -> crate::Runtime) -> *mut crate::Runtime {
|
||||
if RUNTIME.is_null() {
|
||||
let p =
|
||||
std::alloc::alloc(std::alloc::Layout::new::<crate::Runtime>()).cast::<crate::Runtime>();
|
||||
*p = r;
|
||||
RUNTIME = p;
|
||||
RUNTIME = Box::into_raw(Box::new(r()));
|
||||
RUNTIME
|
||||
} else {
|
||||
RUNTIME
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc() {
|
||||
drop(Box::from_raw(RUNTIME));
|
||||
}
|
||||
|
||||
unsafe fn deref(_: i64) -> *mut crate::Runtime {
|
||||
RUNTIME
|
||||
}
|
||||
|
@ -11,10 +11,14 @@ use crate::Runtime as ToadRuntime;
|
||||
pub struct Runtime(java::lang::Object);
|
||||
|
||||
impl Runtime {
|
||||
pub fn init(e: &mut java::Env, cfg: RuntimeConfig) -> i64 {
|
||||
let r =
|
||||
ToadRuntime::try_new(format!("0.0.0.0:{}", cfg.net(e).port(e)), cfg.to_toad(e)).unwrap();
|
||||
unsafe { crate::mem::Runtime::alloc(r).addr() as i64 }
|
||||
pub fn get_or_init(e: &mut java::Env, cfg: RuntimeConfig) -> Self {
|
||||
static GET_OR_INIT: java::StaticMethod<Runtime, fn(RuntimeConfig) -> Runtime> = java::StaticMethod::new("getOrInit");
|
||||
GET_OR_INIT.invoke(e, cfg)
|
||||
}
|
||||
|
||||
pub fn poll_req(&self, e: &mut java::Env) -> Option<MessageRef> {
|
||||
static POLL_REQ: java::Method<Runtime, fn() -> java::util::Optional<MessageRef>> = java::Method::new("pollReq");
|
||||
POLL_REQ.invoke(e, self).to_option(e)
|
||||
}
|
||||
|
||||
pub fn addr(&self, e: &mut java::Env) -> i64 {
|
||||
@ -25,6 +29,27 @@ impl Runtime {
|
||||
pub fn ref_(&self, e: &mut java::Env) -> &'static ToadRuntime {
|
||||
unsafe { crate::mem::Runtime::deref(self.addr(e)).as_ref().unwrap() }
|
||||
}
|
||||
|
||||
fn init_impl(e: &mut java::Env, cfg: RuntimeConfig) -> i64 {
|
||||
let r = ||
|
||||
ToadRuntime::try_new(format!("0.0.0.0:{}", cfg.net(e).port(e)), cfg.to_toad(e)).unwrap();
|
||||
unsafe { crate::mem::Runtime::alloc(r).addr() as i64 }
|
||||
}
|
||||
|
||||
fn poll_req_impl(&self, e: &mut java::Env) -> java::util::Optional<MessageRef> {
|
||||
match self.ref_(e).poll_req() {
|
||||
| Ok(req) => {
|
||||
let mr = MessageRef::new(e, req.data().msg());
|
||||
java::util::Optional::<MessageRef>::of(e, mr)
|
||||
},
|
||||
| Err(nb::Error::WouldBlock) => {
|
||||
java::util::Optional::<MessageRef>::empty(e)
|
||||
},
|
||||
| Err(nb::Error::Other(err)) => {
|
||||
e.throw(format!("{:?}", err)).unwrap();
|
||||
java::util::Optional::<MessageRef>::empty(e)
|
||||
},
|
||||
} }
|
||||
}
|
||||
|
||||
java::object_newtype!(Runtime);
|
||||
@ -41,29 +66,18 @@ pub extern "system" fn Java_dev_toad_Runtime_init<'local>(mut e: java::Env<'loca
|
||||
let e = &mut e;
|
||||
let cfg = java::lang::Object::from_local(e, cfg).upcast_to::<RuntimeConfig>(e);
|
||||
|
||||
Runtime::init(e, cfg)
|
||||
Runtime::init_impl(e, cfg)
|
||||
}
|
||||
|
||||
// JNIEXPORT jobject JNICALL Java_dev_toad_Runtime_pollReq
|
||||
// (JNIEnv *, jobject, jobject);
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_dev_toad_Runtime_pollReq<'local>(mut e: java::Env<'local>,
|
||||
runtime: JObject<'local>,
|
||||
cfg: JObject<'local>)
|
||||
runtime: JObject<'local>)
|
||||
-> jobject {
|
||||
let e = &mut e;
|
||||
let runtime = java::lang::Object::from_local(e, runtime).upcast_to::<Runtime>(e);
|
||||
match runtime.ref_(e).poll_req() {
|
||||
| Ok(req) => {
|
||||
let mr = MessageRef::new(e, req.data().msg());
|
||||
java::util::Optional::<MessageRef>::of(e, mr).downcast(e)
|
||||
.as_raw()
|
||||
},
|
||||
| Err(nb::Error::WouldBlock) => java::util::Optional::<MessageRef>::empty(e).downcast(e)
|
||||
.as_raw(),
|
||||
| Err(nb::Error::Other(err)) => {
|
||||
e.throw(format!("{:?}", err)).unwrap();
|
||||
core::ptr::null_mut()
|
||||
},
|
||||
}
|
||||
java::lang::Object::from_local(e, runtime)
|
||||
.upcast_to::<Runtime>(e)
|
||||
.poll_req_impl(e)
|
||||
.downcast(e)
|
||||
.to_local(e)
|
||||
.as_raw()
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use toad::time::Millis;
|
||||
use toad_jni::java;
|
||||
|
||||
use crate::retry_strategy::RetryStrategy;
|
||||
use crate::uint;
|
||||
|
||||
pub struct RuntimeConfig(java::lang::Object);
|
||||
|
||||
@ -74,15 +75,21 @@ impl java::Class for Net {
|
||||
const PATH: &'static str = concat!(package!(dev.toad.RuntimeOptions), "$Net");
|
||||
}
|
||||
|
||||
static NET_PORT: java::Field<Net, uint::u16> = java::Field::new("port");
|
||||
|
||||
impl Net {
|
||||
pub fn port(&self, e: &mut java::Env) -> i16 {
|
||||
static PORT: java::Method<Net, fn() -> i16> = java::Method::new("port");
|
||||
PORT.invoke(e, self)
|
||||
pub fn port(&self, e: &mut java::Env) -> u16 {
|
||||
NET_PORT.get(e, self).to_rust(e)
|
||||
}
|
||||
|
||||
pub fn concurrency(&self, e: &mut java::Env) -> i16 {
|
||||
static CONCURRENCY: java::Method<Net, fn() -> i16> = java::Method::new("concurrency");
|
||||
CONCURRENCY.invoke(e, self)
|
||||
pub fn set_port(&self, e: &mut java::Env, new: u16) {
|
||||
let new = uint::u16::from_rust(e, new);
|
||||
NET_PORT.set(e, self, new)
|
||||
}
|
||||
|
||||
pub fn concurrency(&self, e: &mut java::Env) -> u8 {
|
||||
static CONCURRENCY: java::Field<Net, uint::u8> = java::Field::new("concurrency");
|
||||
CONCURRENCY.get(e, self).to_rust(e)
|
||||
}
|
||||
|
||||
pub fn msg(&self, e: &mut java::Env) -> Msg {
|
||||
|
@ -5,10 +5,18 @@ import java.util.Optional;
|
||||
|
||||
public class Runtime {
|
||||
|
||||
static {
|
||||
System.loadLibrary("toad_java_glue");
|
||||
}
|
||||
|
||||
private final long addr;
|
||||
|
||||
private static native long init(RuntimeOptions o);
|
||||
private native Optional<MessageRef> pollReq(RuntimeOptions o);
|
||||
private native Optional<MessageRef> pollReq();
|
||||
|
||||
public static Runtime getOrInit(RuntimeOptions o) {
|
||||
return new Runtime(o);
|
||||
}
|
||||
|
||||
public Runtime(RuntimeOptions o) {
|
||||
this.addr = Runtime.init(o);
|
||||
|
Loading…
Reference in New Issue
Block a user