| #![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, |
| )); |
| } |
| } |