diff --git a/build.sbt b/build.sbt index bde56e5..1cfff06 100644 --- a/build.sbt +++ b/build.sbt @@ -26,6 +26,14 @@ lazy val root = project "java.sources" -> baseDirectory.value.toGlob / "src" / "main" / "java" / ** / "*.java", "glue.sources" -> baseDirectory.value.toGlob / "glue" / "src" / ** / "*.rs" ), + path := Map( + "glue.base" -> (baseDirectory.value / "glue").toString, + "glue.target" -> (baseDirectory.value / "target" / "glue" / "debug").toString, + "java.classTarget" -> (baseDirectory.value / "target" / "scala-3.2.2" / "classes").toString + ), + Test / javaOptions ++= Seq( + "-Djava.library.path="++path.value("glue.target"), + ), Compile / doc / javacOptions ++= Seq( "--enable-preview", "--release", @@ -38,11 +46,6 @@ lazy val root = project "-Xlint:unchecked", "-Xlint:deprecation" ), - path := Map( - "glue.base" -> (baseDirectory.value / "glue").toString, - "glue.target" -> (baseDirectory.value / "target" / "glue" / "debug").toString, - "java.classTarget" -> (baseDirectory.value / "target" / "scala-3.2.2" / "classes").toString - ), ejectHeaders := { val files = FileTreeView.default.iterator(glob.value("java.sources")).foldLeft("") { diff --git a/glue/Cargo.lock b/glue/Cargo.lock index 06cbdb2..f81de88 100644 --- a/glue/Cargo.lock +++ b/glue/Cargo.lock @@ -570,6 +570,8 @@ name = "toad-java-glue" version = "0.1.0" dependencies = [ "jni", + "log", + "naan", "nb", "no-std-net", "tinyvec", @@ -580,10 +582,11 @@ dependencies = [ [[package]] name = "toad-jni" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef61b22c3478d481635b9eb67496d4dc18f22b8321961aca5dc2084f5253ccb7" +checksum = "8e6f9b7ff8462ec97df69ef2bb01b485626fb43ce62a8fe33c6179f3537a9d38" dependencies = [ + "embedded-time", "jni", "nb", "no-std-net", @@ -591,6 +594,7 @@ dependencies = [ "toad", "toad-array 0.5.0", "toad-len", + "toad-msg", "toad-stem", ] diff --git a/glue/Cargo.toml b/glue/Cargo.toml index 21a33e5..ce23fc2 100644 --- a/glue/Cargo.toml +++ b/glue/Cargo.toml @@ -15,7 +15,9 @@ e2e = [] jni = "0.21.1" nb = "1" toad = "0.17.3" -toad-jni = "0.9.1" +toad-jni = "0.10.1" no-std-net = "0.6" toad-msg = "0.18.1" tinyvec = {version = "1.5", default_features = false, features = ["rustc_1_55"]} +naan = "0.1.32" +log = "0.4.17" diff --git a/glue/src/dev/toad/mod.rs b/glue/src/dev/toad/mod.rs index cae2e85..bf97eb8 100644 --- a/glue/src/dev/toad/mod.rs +++ b/glue/src/dev/toad/mod.rs @@ -10,6 +10,7 @@ pub use retry_strategy::RetryStrategy; use toad::platform::Platform; use toad::retry::{Attempts, Strategy}; use toad::time::Millis; +use toad_jni::java::nio::channels::{DatagramChannel, PeekableDatagramChannel}; use toad_jni::java::{self, Object}; use crate::mem::{Shared, SharedMemoryRegion}; @@ -18,9 +19,10 @@ use crate::Runtime; pub struct Toad(java::lang::Object); impl Toad { - pub fn new(e: &mut java::Env, cfg: Config) -> Self { - static CTOR: java::Constructor = java::Constructor::new(); - CTOR.invoke(e, cfg) + pub fn new(e: &mut java::Env, cfg: Config, channel: PeekableDatagramChannel) -> Self { + static CTOR: java::Constructor = + java::Constructor::new(); + CTOR.invoke(e, cfg, channel) } pub fn poll_req(&self, e: &mut java::Env) -> Option { @@ -34,8 +36,8 @@ impl Toad { CONFIG.invoke(e, self) } - fn init_impl(e: &mut java::Env, cfg: Config) -> i64 { - let r = || Runtime::try_new(cfg.addr(e), cfg.to_toad(e)).unwrap(); + fn init_impl(e: &mut java::Env, cfg: Config, channel: PeekableDatagramChannel) -> i64 { + let r = || Runtime::new(cfg.to_toad(e), channel); unsafe { crate::mem::Shared::init(r).addr() as i64 } } @@ -49,7 +51,8 @@ impl Toad { }, | Err(nb::Error::WouldBlock) => java::util::Optional::::empty(e), | Err(nb::Error::Other(err)) => { - e.throw(format!("{:?}", err)).unwrap(); + let err = err.downcast_ref(e).to_local(e); + e.throw(jni::objects::JThrowable::from(err)).unwrap(); java::util::Optional::::empty(e) }, } @@ -70,11 +73,6 @@ impl java::Class for Config { } impl Config { - pub fn addr(&self, e: &mut java::Env) -> SocketAddr { - static ADDRESS: java::Field = java::Field::new("addr"); - ADDRESS.get(e, self).to_std(e) - } - pub fn concurrency(&self, e: &mut java::Env) -> u8 { static RUNTIME_CONFIG_CONCURRENCY: java::Field = java::Field::new("concurrency"); @@ -86,9 +84,8 @@ impl Config { RUNTIME_CONFIG_MSG.invoke(e, self) } - pub fn new(e: &mut java::Env, c: toad::config::Config, addr: SocketAddr) -> Self { - static CTOR: java::Constructor = - java::Constructor::new(); + pub fn new(e: &mut java::Env, c: toad::config::Config) -> Self { + static CTOR: java::Constructor = java::Constructor::new(); let con = Con::new(e, c.msg.con.unacked_retry_strategy, @@ -104,9 +101,7 @@ impl Config { let concurrency = ffi::u8::from_rust(e, c.max_concurrent_requests); - let address = java::net::InetSocketAddress::from_std(e, addr); - - let jcfg = CTOR.invoke(e, address, concurrency, msg); + let jcfg = CTOR.invoke(e, concurrency, msg); jcfg } @@ -264,20 +259,20 @@ impl Non { pub extern "system" fn Java_dev_toad_Toad_defaultConfigImpl<'local>(mut env: java::Env<'local>, _: JClass<'local>) -> jobject { - Config::new(&mut env, - toad::config::Config::default(), - SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 5683)).yield_to_java(&mut env) + Config::new(&mut env, toad::config::Config::default()).yield_to_java(&mut env) } #[no_mangle] pub extern "system" fn Java_dev_toad_Toad_init<'local>(mut e: java::Env<'local>, _: JClass<'local>, + channel: JObject<'local>, cfg: JObject<'local>) -> i64 { let e = &mut e; let cfg = java::lang::Object::from_local(e, cfg).upcast_to::(e); + let channel = java::lang::Object::from_local(e, channel).upcast_to::(e); - Toad::init_impl(e, cfg) + Toad::init_impl(e, cfg, channel.peekable()) } #[no_mangle] diff --git a/glue/src/dev/toad/msg/ref_/msg.rs b/glue/src/dev/toad/msg/ref_/msg.rs index 3fa6059..424581b 100644 --- a/glue/src/dev/toad/msg/ref_/msg.rs +++ b/glue/src/dev/toad/msg/ref_/msg.rs @@ -119,10 +119,10 @@ pub extern "system" fn Java_dev_toad_msg_ref_Message_payload<'local>(mut env: ja } #[no_mangle] -pub extern "system" fn Java_dev_toad_msg_ref_Message_type<'local>(mut e: java::Env<'local>, - _: JClass<'local>, - addr: i64) - -> jobject { +pub extern "system" fn Java_dev_toad_msg_ref_Message_typ<'local>(mut e: java::Env<'local>, + _: JClass<'local>, + addr: i64) + -> jobject { let msg = unsafe { Shared::deref::(addr).as_ref() .unwrap() diff --git a/glue/src/e2e.rs b/glue/src/e2e.rs index dde22a2..3b86742 100644 --- a/glue/src/e2e.rs +++ b/glue/src/e2e.rs @@ -1,18 +1,25 @@ +use std::time::{Duration, Instant}; + use no_std_net::SocketAddr; use toad::config::Config; use toad::net::Addrd; use toad::platform::Platform; +use toad_jni::java::net::StandardProtocolFamily::INet; +use toad_jni::java::nio::channels::{DatagramChannel, PeekableDatagramChannel}; use toad_jni::java::{self, Object, Signature}; use toad_msg::alloc::Message; use toad_msg::{Code, Id, Token, Type}; use crate::{dev, Runtime}; +type RustRuntime = + toad::std::Platform>; + #[non_exhaustive] struct State { pub runtime: dev::toad::Toad, pub env: java::Env<'static>, - pub client: crate::Runtime, + pub client: RustRuntime, pub srv_addr: SocketAddr, } @@ -20,17 +27,15 @@ fn init() -> State { let mut _env = crate::test::init(); let env = &mut _env; - let cfg = - dev::toad::Config::new(env, - Config::default(), - std::net::SocketAddr::new(std::net::Ipv4Addr::UNSPECIFIED.into(), 5683)); - let runtime = dev::toad::Toad::new(env, cfg); - let client = Runtime::try_new("0.0.0.0:5684", Default::default()).unwrap(); + let cfg = dev::toad::Config::new(env, Config::default()); + let sock = ::bind(no_std_net::SocketAddr::new(no_std_net::Ipv4Addr::LOCALHOST.into(), 5683)).unwrap(); + let runtime = dev::toad::Toad::new(env, cfg, sock); + let client = RustRuntime::try_new("127.0.0.1:5684", Default::default()).unwrap(); State { runtime, env: _env, client, - srv_addr: "0.0.0.0:5683".parse().unwrap() } + srv_addr: "127.0.0.1:5683".parse().unwrap() } } fn runtime_poll_req(State { runtime, @@ -43,7 +48,14 @@ fn runtime_poll_req(State { runtime, let request = Message::new(Type::Con, Code::GET, Id(0), Token(Default::default())); client.send_msg(Addrd(request, *srv_addr)).unwrap(); - assert!(runtime.poll_req(env).is_some()); + let start = Instant::now(); + loop { + if Instant::now() - start > Duration::from_millis(10000) { + panic!("timed out waiting for DatagramChannel to receive message"); + } else if runtime.poll_req(env).is_some() { + break; + } + } } #[test] diff --git a/glue/src/lib.rs b/glue/src/lib.rs index e05733b..be2f615 100644 --- a/glue/src/lib.rs +++ b/glue/src/lib.rs @@ -8,11 +8,16 @@ use mem::SharedMemoryRegion; mod runtime { use std::collections::BTreeMap; - use toad::platform::Effect; - use toad::std::{dtls, Platform}; - use toad::step::runtime::std::Runtime as DefaultSteps; + use toad::config::Config; + use toad::net::Addrd; + use toad::platform::{Effect, Platform}; + use toad::req::Req; + use toad::resp::Resp; + use toad::step::runtime::Runtime as DefaultSteps; + use toad_jni::java::io::IOException; + use toad_jni::java::lang::Throwable; use toad_jni::java::nio::channels::PeekableDatagramChannel; - use toad_msg::{OptValue, OptNumber}; + use toad_msg::{OptNumber, OptValue}; #[derive(Clone, Copy, Debug)] pub struct PlatformTypes; @@ -27,7 +32,49 @@ mod runtime { type Effects = Vec>; } - pub type Runtime = Platform>; + type Steps = DefaultSteps; + + pub struct Runtime { + steps: Steps, + config: Config, + channel: PeekableDatagramChannel, + clock: toad::std::Clock, + } + + impl Runtime { + pub fn new(config: Config, channel: PeekableDatagramChannel) -> Self { + Self { steps: Default::default(), + config, + channel, + clock: toad::std::Clock::new() } + } + } + + impl Platform for Runtime { + type Types = PlatformTypes; + type Error = IOException; + + fn log(&self, level: log::Level, msg: toad::todo::String<1000>) -> Result<(), Self::Error> { + println!("[{}]: {}", level, msg.as_str()); + Ok(()) + } + + fn config(&self) -> toad::config::Config { + self.config + } + + fn steps(&self) -> &Steps { + &self.steps + } + + fn socket(&self) -> &PeekableDatagramChannel { + &self.channel + } + + fn clock(&self) -> &toad::std::Clock { + &self.clock + } + } } pub use runtime::Runtime; @@ -59,21 +106,35 @@ pub mod e2e; #[cfg(test)] pub mod test { use std::net::{Ipv4Addr, SocketAddr}; + use std::path::PathBuf; + use std::process::Command; use std::sync::Once; use jni::{InitArgsBuilder, JavaVM}; use toad::config::Config; use toad::retry::Strategy; use toad::time::Millis; - use toad_jni::java; + use toad_jni::java::{self, Class, ResultExt}; use crate::dev; pub fn init<'a>() -> java::Env<'a> { static INIT: Once = Once::new(); INIT.call_once(|| { + let repo_root = Command::new("git").arg("rev-parse") + .arg("--show-toplevel") + .output() + .unwrap(); + assert!(repo_root.status.success()); + + let lib_path = String::from_utf8(repo_root.stdout).unwrap() + .trim() + .to_string(); + let lib_path = PathBuf::from(lib_path).join("target/glue/debug"); + let jvm = - JavaVM::new(InitArgsBuilder::new().option("-Djava.library.path=/home/orion/src/toad-lib/toad-java/target/glue/debug/") + JavaVM::new(InitArgsBuilder::new().option(format!("-Djava.library.path={}", + lib_path.to_string_lossy())) .option("-Djava.class.path=../target/scala-3.2.2/classes") .option("--enable-preview") .build() @@ -81,8 +142,13 @@ pub mod test { toad_jni::global::init_with(jvm); }); - toad_jni::global::jvm().attach_current_thread_permanently() - .unwrap() + let mut env = toad_jni::global::jvm().attach_current_thread_permanently() + .unwrap(); + + env.call_static_method(crate::dev::toad::Toad::PATH, "loadNativeLib", "()V", &[]) + .unwrap_java(&mut env); + + env } #[test] @@ -97,9 +163,7 @@ pub mod test { let mut e = init(); let e = &mut e; - let r = dev::toad::Config::new(e, - Config::default(), - SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 5683)); + let r = dev::toad::Config::new(e, Config::default()); assert_eq!(r.to_toad(e), Config::default()); } diff --git a/src/main/java/dev.toad/Toad.java b/src/main/java/dev.toad/Toad.java index e8a72c0..917c7c2 100644 --- a/src/main/java/dev.toad/Toad.java +++ b/src/main/java/dev.toad/Toad.java @@ -25,6 +25,10 @@ public final class Toad implements AutoCloseable { } static { + Toad.loadNativeLib(); + } + + static void loadNativeLib() { System.loadLibrary("toad_java_glue"); } @@ -32,25 +36,25 @@ public final class Toad implements AutoCloseable { final Config config; final DatagramChannel channel; - static native long init(Config o); + static native long init(DatagramChannel chan, Config o); - native Optional pollReq(long ptr); + static native Optional pollReq(long ptr); - native Optional pollResp( + static native Optional pollResp( long ptr, dev.toad.msg.Token t, InetSocketAddress n ); - Optional pollReq() { - return this.pollReq(this.ptr.addr()); + public Optional pollReq() { + return Toad.pollReq(this.ptr.addr()); } - Optional pollResp( + public Optional pollResp( dev.toad.msg.Token regarding, InetSocketAddress from ) { - return this.pollResp(this.ptr.addr(), regarding, from); + return Toad.pollResp(this.ptr.addr(), regarding, from); } public static BuilderRequiresSocket builder() { @@ -60,7 +64,7 @@ public final class Toad implements AutoCloseable { Toad(Config o, DatagramChannel channel) { this.config = o; this.channel = channel; - this.ptr = Ptr.register(this.getClass(), this.init(o)); + this.ptr = Ptr.register(this.getClass(), this.init(this.channel, o)); } public Config config() { diff --git a/src/main/java/dev.toad/msg/ref/Message.java b/src/main/java/dev.toad/msg/ref/Message.java index 31096bb..63848af 100644 --- a/src/main/java/dev.toad/msg/ref/Message.java +++ b/src/main/java/dev.toad/msg/ref/Message.java @@ -30,7 +30,7 @@ public final class Message implements dev.toad.msg.Message, AutoCloseable { static native Code code(long addr); - static native Type type(long addr); + static native Type typ(long addr); static native dev.toad.msg.ref.Option[] opts(long addr); @@ -63,7 +63,7 @@ public final class Message implements dev.toad.msg.Message, AutoCloseable { } public Type type() { - return this.type(this.ptr.addr()); + return this.typ(this.ptr.addr()); } public dev.toad.msg.ref.Option[] optionRefs() { diff --git a/src/test/scala/E2E.scala b/src/test/scala/E2E.scala index 003eb7a..62b4ed7 100644 --- a/src/test/scala/E2E.scala +++ b/src/test/scala/E2E.scala @@ -3,6 +3,8 @@ import mock.java.nio.channels.Mock; class E2E extends munit.FunSuite { test("foo") { - val mock = Mock.Channel(); + val mock = Mock.Channel() + val toad = Toad.builder.channel(mock).build + val req = Option.apply(toad.pollReq.get) } }