feat: runtime

This commit is contained in:
Orion Kindel 2023-04-08 22:54:04 -07:00
parent 7eef137bcd
commit b400124096
Signed by untrusted user who does not match committer: orion
GPG Key ID: 6D4165AE4C928719
9 changed files with 139 additions and 48 deletions

View File

@ -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
View File

@ -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",

View File

@ -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
View 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);
}

View File

@ -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]

View File

@ -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
}

View File

@ -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)
java::lang::Object::from_local(e, runtime)
.upcast_to::<Runtime>(e)
.poll_req_impl(e)
.downcast(e)
.to_local(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()
},
}
}

View File

@ -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 {

View File

@ -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);