blob: 682950773b19ddbf6e7f688efefc6ebd575b02af [file] [log] [blame]
#![cfg(feature = "invocation")]
use std::{
sync::{Arc, Barrier},
thread::spawn,
};
use jni::{
objects::{AutoLocal, JValue},
sys::jint,
JNIEnv,
};
mod util;
use util::{attach_current_thread, unwrap};
#[test]
pub fn weak_ref_works_in_other_threads() {
const ITERS_PER_THREAD: usize = 10_000;
let mut env = attach_current_thread();
let mut join_handlers = Vec::new();
let atomic_integer_local = AutoLocal::new(
unwrap(
env.new_object(
"java/util/concurrent/atomic/AtomicInteger",
"(I)V",
&[JValue::from(0)],
),
&env,
),
&env,
);
let atomic_integer =
unwrap(env.new_weak_ref(&atomic_integer_local), &env).expect("weak ref should not be null");
// Test with a different number of threads (from 2 to 8)
for thread_num in 2..9 {
let barrier = Arc::new(Barrier::new(thread_num));
for _ in 0..thread_num {
let barrier = barrier.clone();
let atomic_integer = atomic_integer.clone();
let jh = spawn(move || {
let mut env = attach_current_thread();
barrier.wait();
for _ in 0..ITERS_PER_THREAD {
let atomic_integer = env.auto_local(
unwrap(atomic_integer.upgrade_local(&env), &env)
.expect("AtomicInteger shouldn't have been GC'd yet"),
);
unwrap(
unwrap(
env.call_method(&atomic_integer, "incrementAndGet", "()I", &[]),
&env,
)
.i(),
&env,
);
}
});
join_handlers.push(jh);
}
for jh in join_handlers.drain(..) {
jh.join().unwrap();
}
let expected = (ITERS_PER_THREAD * thread_num) as jint;
assert_eq!(
expected,
unwrap(
unwrap(
env.call_method(
&atomic_integer_local,
"getAndSet",
"(I)I",
&[JValue::from(0)]
),
&env,
)
.i(),
&env,
)
);
}
}
#[test]
fn weak_ref_is_actually_weak() {
let mut env = attach_current_thread();
// This test uses `with_local_frame` to work around issue #109.
fn run_gc(env: &mut JNIEnv) {
unwrap(
env.with_local_frame(1, |env| {
env.call_static_method("java/lang/System", "gc", "()V", &[])?;
Ok(())
}),
env,
);
}
for _ in 0..100 {
let obj_local = unwrap(
env.with_local_frame_returning_local(2, |env| {
env.new_object("java/lang/Object", "()V", &[])
}),
&env,
);
let obj_local = env.auto_local(obj_local);
let obj_weak =
unwrap(env.new_weak_ref(&obj_local), &env).expect("weak ref should not be null");
let obj_weak2 =
unwrap(obj_weak.clone_in_jvm(&env), &env).expect("weak ref should not be null");
run_gc(&mut env);
for obj_weak in &[&obj_weak, &obj_weak2] {
{
let obj_local_from_weak = env.auto_local(
unwrap(obj_weak.upgrade_local(&env), &env)
.expect("object shouldn't have been GC'd yet"),
);
assert!(!obj_local_from_weak.is_null());
assert!(unwrap(
env.is_same_object(&obj_local_from_weak, &obj_local),
&env,
));
}
{
let obj_global_from_weak = unwrap(obj_weak.upgrade_global(&env), &env)
.expect("object shouldn't have been GC'd yet");
assert!(!obj_global_from_weak.is_null());
assert!(unwrap(
env.is_same_object(&obj_global_from_weak, &obj_local),
&env,
));
}
assert!(unwrap(obj_weak.is_same_object(&env, &obj_local), &env));
assert!(
!unwrap(obj_weak.is_garbage_collected(&env), &env),
"`is_garbage_collected` returned incorrect value"
);
}
assert!(unwrap(
obj_weak.is_weak_ref_to_same_object(&env, &obj_weak2),
&env,
));
drop(obj_local);
run_gc(&mut env);
for obj_weak in &[&obj_weak, &obj_weak2] {
{
let obj_local_from_weak = unwrap(obj_weak.upgrade_local(&env), &env);
assert!(
obj_local_from_weak.is_none(),
"object should have been GC'd"
);
}
{
let obj_global_from_weak = unwrap(obj_weak.upgrade_global(&env), &env);
assert!(
obj_global_from_weak.is_none(),
"object should have been GC'd"
);
}
assert!(
unwrap(obj_weak.is_garbage_collected(&env), &env),
"`is_garbage_collected` returned incorrect value"
);
}
assert!(unwrap(
obj_weak.is_weak_ref_to_same_object(&env, &obj_weak2),
&env,
));
}
}