Skip to content

Commit d8b6256

Browse files
committed
A mostly naive implementation of select, because of absence for selecting among mpsc channels: rust-lang/rust#27800.
1 parent 1446ea5 commit d8b6256

File tree

6 files changed

+131
-19
lines changed

6 files changed

+131
-19
lines changed

java/src/main/java/org/astonbitecode/j4rs/api/jfx/handlers/J4rsEventHandler.java

-5
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,8 @@
1919
import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport;
2020

2121
public class J4rsEventHandler<T extends Event> extends NativeCallbackToRustChannelSupport implements EventHandler<T> {
22-
public J4rsEventHandler() {
23-
System.out.println("------NEW---");
24-
}
25-
2622
@Override
2723
public void handle(T event) {
28-
System.out.println("---------");
2924
doCallback(event);
3025
}
3126
}
Binary file not shown.

rust/src/api.rs

+97-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
use std::{fs, mem};
15+
use std::{fs, mem, thread, time};
1616
use std::any::Any;
1717
use std::convert::TryFrom;
1818
use std::env;
@@ -1013,6 +1013,44 @@ impl Jvm {
10131013
}
10141014
}
10151015
}
1016+
1017+
/// Returns the first `Instance` that is available from the passed `InstanceReceiver`s,
1018+
/// along with the index of the receiver that was selected and actually returned the instance.
1019+
///
1020+
/// This is a mostly naive implementation of select, because of [absence for selecting among mpsc channels](https://github.com/rust-lang/rust/issues/27800).
1021+
pub fn select(instance_receivers: &[&InstanceReceiver]) -> errors::Result<(usize, Instance)> {
1022+
loop {
1023+
for (index, ir) in instance_receivers.iter().enumerate() {
1024+
let res = ir.rx.try_recv();
1025+
if res.is_ok() {
1026+
return Ok((index, res.unwrap()));
1027+
}
1028+
}
1029+
thread::yield_now();
1030+
}
1031+
}
1032+
1033+
/// Returns the first `Instance` that is available from the passed `InstanceReceiver`s,
1034+
/// along with the index of the receiver that was selected and actually returned the instance.
1035+
///
1036+
/// If there are no instances returned for the duration defined in timeout argument, an error is returned.
1037+
///
1038+
/// This is a mostly naive implementation of select, because of [absence for selecting among mpsc channels](https://github.com/rust-lang/rust/issues/27800).
1039+
pub fn select_timeout(instance_receivers: &[&InstanceReceiver], timeout: &time::Duration) -> errors::Result<(usize, Instance)> {
1040+
let start = time::Instant::now();
1041+
loop {
1042+
for (index, ir) in instance_receivers.iter().enumerate() {
1043+
let res = ir.rx.try_recv();
1044+
if res.is_ok() {
1045+
return Ok((index, res.unwrap()));
1046+
}
1047+
}
1048+
if &start.elapsed() > timeout {
1049+
return Err(errors::J4RsError::Timeout);
1050+
}
1051+
thread::yield_now();
1052+
}
1053+
}
10161054
}
10171055

10181056
impl Drop for Jvm {
@@ -1739,11 +1777,13 @@ impl InstanceReceiver {
17391777

17401778
impl Drop for InstanceReceiver {
17411779
fn drop(&mut self) {
1742-
debug("Dropping an InstanceReceiver");
1743-
let p = self.tx_address as *mut Sender<Instance>;
1744-
unsafe {
1745-
let tx = Box::from_raw(p);
1746-
mem::drop(tx);
1780+
if self.tx_address > 0 {
1781+
debug("Dropping an InstanceReceiver");
1782+
let p = self.tx_address as *mut Sender<Instance>;
1783+
unsafe {
1784+
let tx = Box::from_raw(p);
1785+
mem::drop(tx);
1786+
}
17471787
}
17481788
}
17491789
}
@@ -1867,7 +1907,7 @@ impl<'a> ChainableInstance<'a> {
18671907

18681908
fn new_with_instance_ref(instance: &Instance, jvm: &'a Jvm) -> errors::Result<ChainableInstance<'a>> {
18691909
let cloned = jvm.clone_instance(&instance)?;
1870-
Ok(ChainableInstance { instance: cloned, jvm })
1910+
Ok(ChainableInstance { instance: cloned, jvm })
18711911
}
18721912

18731913
pub fn collect(self) -> Instance {
@@ -2020,6 +2060,56 @@ mod api_unit_tests {
20202060
let _ = fs_extra::remove_items(&vec![newdir]);
20212061
}
20222062

2063+
#[test]
2064+
fn test_select() {
2065+
let (tx1, rx1) = channel();
2066+
let ir1 = InstanceReceiver::new(rx1, 0);
2067+
let (_tx2, rx2) = channel();
2068+
let ir2 = InstanceReceiver::new(rx2, 0);
2069+
let (tx3, rx3) = channel();
2070+
let ir3 = InstanceReceiver::new(rx3, 0);
2071+
2072+
thread::spawn(move || {
2073+
let _ = tx3.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2074+
// Block the thread as sending does not block the current thread
2075+
thread::sleep(time::Duration::from_millis(10));
2076+
let _ = tx1.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2077+
thread::sleep(time::Duration::from_millis(10));
2078+
let _ = tx3.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2079+
});
2080+
2081+
let (index1, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2082+
let (index2, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2083+
let (index3, _) = Jvm::select(&[&ir1, &ir2, &ir3]).unwrap();
2084+
assert!(index1 == 2);
2085+
assert!(index2 == 0);
2086+
assert!(index3 == 2);
2087+
}
2088+
2089+
#[test]
2090+
fn test_select_timeout() {
2091+
let (tx1, rx1) = channel();
2092+
let ir1 = InstanceReceiver::new(rx1, 0);
2093+
let (tx2, rx2) = channel();
2094+
let ir2 = InstanceReceiver::new(rx2, 0);
2095+
2096+
thread::spawn(move || {
2097+
let _ = tx1.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2098+
// Block the thread as sending does not block the current thread
2099+
thread::sleep(time::Duration::from_millis(10));
2100+
let _ = tx2.send(Instance::new(ptr::null_mut(), CLASS_STRING).unwrap());
2101+
});
2102+
2103+
let d = time::Duration::from_millis(500);
2104+
let (index1, _) = Jvm::select_timeout(&[&ir1, &ir2], &d).unwrap();
2105+
let (index2, _) = Jvm::select_timeout(&[&ir1, &ir2], &d).unwrap();
2106+
assert!(Jvm::select_timeout(&[&ir1, &ir2], &d).is_err());
2107+
dbg!(index1);
2108+
dbg!(index2);
2109+
assert!(index1 == 0);
2110+
assert!(index2 == 1);
2111+
}
2112+
20232113
fn validate_type(ia: InvocationArg, class: &str) {
20242114
let b = match ia {
20252115
_s @ InvocationArg::Java { .. } => false,

rust/src/errors.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::{fmt, result};
1516
use std::convert::Infallible;
1617
use std::error::Error;
1718
use std::ffi::NulError;
1819
use std::io;
1920
use std::sync::{PoisonError, TryLockError};
20-
use std::{fmt, result};
2121

2222
use fs_extra;
2323
use serde_json;
@@ -46,6 +46,7 @@ pub enum J4RsError {
4646
JniError(String),
4747
RustError(String),
4848
ParseError(String),
49+
Timeout,
4950
}
5051

5152
impl fmt::Display for J4RsError {
@@ -56,6 +57,7 @@ impl fmt::Display for J4RsError {
5657
&J4RsError::JniError(ref message) => write!(f, "{}", message),
5758
&J4RsError::RustError(ref message) => write!(f, "{}", message),
5859
&J4RsError::ParseError(ref message) => write!(f, "{}", message),
60+
&J4RsError::Timeout => write!(f, "Timeout"),
5961
}
6062
}
6163
}
@@ -68,6 +70,7 @@ impl Error for J4RsError {
6870
J4RsError::JniError(_) => "A JNI error occured",
6971
J4RsError::RustError(_) => "An error coming from Rust occured",
7072
J4RsError::ParseError(_) => "A parsing error occured",
73+
J4RsError::Timeout => "Timeout",
7174
}
7275
}
7376
}

rust/src/jfx.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::convert::TryFrom;
12
// Copyright 2020 astonbitecode
23
//
34
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,9 +13,8 @@
1213
// See the License for the specific language governing permissions and
1314
// limitations under the License.
1415
use std::env;
15-
use std::convert::TryFrom;
1616

17-
use crate::{InstanceReceiver, Jvm, MavenArtifact, Instance, InvocationArg};
17+
use crate::{Instance, InstanceReceiver, InvocationArg, Jvm, MavenArtifact};
1818
use crate::errors;
1919
use crate::errors::J4RsError;
2020

@@ -39,9 +39,8 @@ impl JavaFxSupport for Jvm {
3939
let fx_callback = self.create_instance(
4040
"org.astonbitecode.j4rs.api.jfx.FxApplicationStartCallback",
4141
&[])?;
42-
let cb = self.init_callback_channel(&fx_callback)?;
43-
self.invoke(&fx_callback, "setCallbackToApplicationAndLaunch", &[])?;
44-
Ok(cb)
42+
43+
self.invoke_to_channel(&fx_callback, "setCallbackToApplicationAndLaunch", &[])
4544
}
4645

4746
fn set_javafx_event_receiver(&self, instance: &Instance, method: &str) -> errors::Result<InstanceReceiver> {
@@ -88,9 +87,10 @@ fn maven(s: &str, jvm: &Jvm) {
8887

8988
#[cfg(test)]
9089
mod api_unit_tests {
91-
use super::*;
9290
use crate::JvmBuilder;
9391

92+
use super::*;
93+
9494
#[test]
9595
#[should_panic]
9696
fn test_deploy_javafx_dependencies() {

rust/src/lib.rs

+24
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,30 @@ mod lib_unit_tests {
228228
}
229229
}
230230

231+
// #[test]
232+
// #[ignore]
233+
fn _memory_leaks_invoke_instances_to_channel() {
234+
let jvm: Jvm = super::new_jvm(Vec::new(), Vec::new()).unwrap();
235+
match jvm.create_instance("org.astonbitecode.j4rs.tests.MySecondTest", Vec::new().as_ref()) {
236+
Ok(instance) => {
237+
for i in 0..100000000 {
238+
let instance_receiver = jvm.invoke_to_channel(&instance, "performCallback", &[]).unwrap();
239+
let thousand_millis = time::Duration::from_millis(1000);
240+
let res = instance_receiver.rx().recv_timeout(thousand_millis);
241+
if i % 100000 == 0 {
242+
println!("{}: {}", i, res.is_ok());
243+
}
244+
}
245+
}
246+
Err(error) => {
247+
panic!("ERROR when creating Instance: {:?}", error);
248+
}
249+
}
250+
251+
let thousand_millis = time::Duration::from_millis(1000);
252+
thread::sleep(thousand_millis);
253+
}
254+
231255
#[test]
232256
fn clone_instance() {
233257
let jvm: Jvm = super::new_jvm(vec![ClasspathEntry::new("onemore.jar")], Vec::new()).unwrap();

0 commit comments

Comments
 (0)