1 #![cfg(feature = "invocation")]
2 
3 use std::{
4     sync::{Arc, Barrier},
5     thread::spawn,
6 };
7 
8 use jni::{
9     objects::{AutoLocal, JValue},
10     sys::jint,
11     JNIEnv,
12 };
13 
14 mod util;
15 use util::{attach_current_thread, unwrap};
16 
17 #[test]
weak_ref_works_in_other_threads()18 pub fn weak_ref_works_in_other_threads() {
19     const ITERS_PER_THREAD: usize = 10_000;
20 
21     let mut env = attach_current_thread();
22     let mut join_handlers = Vec::new();
23 
24     let atomic_integer_local = AutoLocal::new(
25         unwrap(
26             env.new_object(
27                 "java/util/concurrent/atomic/AtomicInteger",
28                 "(I)V",
29                 &[JValue::from(0)],
30             ),
31             &env,
32         ),
33         &env,
34     );
35     let atomic_integer =
36         unwrap(env.new_weak_ref(&atomic_integer_local), &env).expect("weak ref should not be null");
37 
38     // Test with a different number of threads (from 2 to 8)
39     for thread_num in 2..9 {
40         let barrier = Arc::new(Barrier::new(thread_num));
41 
42         for _ in 0..thread_num {
43             let barrier = barrier.clone();
44             let atomic_integer = atomic_integer.clone();
45 
46             let jh = spawn(move || {
47                 let mut env = attach_current_thread();
48                 barrier.wait();
49                 for _ in 0..ITERS_PER_THREAD {
50                     let atomic_integer = env.auto_local(
51                         unwrap(atomic_integer.upgrade_local(&env), &env)
52                             .expect("AtomicInteger shouldn't have been GC'd yet"),
53                     );
54                     unwrap(
55                         unwrap(
56                             env.call_method(&atomic_integer, "incrementAndGet", "()I", &[]),
57                             &env,
58                         )
59                         .i(),
60                         &env,
61                     );
62                 }
63             });
64             join_handlers.push(jh);
65         }
66 
67         for jh in join_handlers.drain(..) {
68             jh.join().unwrap();
69         }
70 
71         let expected = (ITERS_PER_THREAD * thread_num) as jint;
72         assert_eq!(
73             expected,
74             unwrap(
75                 unwrap(
76                     env.call_method(
77                         &atomic_integer_local,
78                         "getAndSet",
79                         "(I)I",
80                         &[JValue::from(0)]
81                     ),
82                     &env,
83                 )
84                 .i(),
85                 &env,
86             )
87         );
88     }
89 }
90 
91 #[test]
weak_ref_is_actually_weak()92 fn weak_ref_is_actually_weak() {
93     let mut env = attach_current_thread();
94 
95     // This test uses `with_local_frame` to work around issue #109.
96 
97     fn run_gc(env: &mut JNIEnv) {
98         unwrap(
99             env.with_local_frame(1, |env| {
100                 env.call_static_method("java/lang/System", "gc", "()V", &[])?;
101                 Ok(())
102             }),
103             env,
104         );
105     }
106 
107     for _ in 0..100 {
108         let obj_local = unwrap(
109             env.with_local_frame_returning_local(2, |env| {
110                 env.new_object("java/lang/Object", "()V", &[])
111             }),
112             &env,
113         );
114         let obj_local = env.auto_local(obj_local);
115 
116         let obj_weak =
117             unwrap(env.new_weak_ref(&obj_local), &env).expect("weak ref should not be null");
118 
119         let obj_weak2 =
120             unwrap(obj_weak.clone_in_jvm(&env), &env).expect("weak ref should not be null");
121 
122         run_gc(&mut env);
123 
124         for obj_weak in &[&obj_weak, &obj_weak2] {
125             {
126                 let obj_local_from_weak = env.auto_local(
127                     unwrap(obj_weak.upgrade_local(&env), &env)
128                         .expect("object shouldn't have been GC'd yet"),
129                 );
130 
131                 assert!(!obj_local_from_weak.is_null());
132                 assert!(unwrap(
133                     env.is_same_object(&obj_local_from_weak, &obj_local),
134                     &env,
135                 ));
136             }
137 
138             {
139                 let obj_global_from_weak = unwrap(obj_weak.upgrade_global(&env), &env)
140                     .expect("object shouldn't have been GC'd yet");
141 
142                 assert!(!obj_global_from_weak.is_null());
143                 assert!(unwrap(
144                     env.is_same_object(&obj_global_from_weak, &obj_local),
145                     &env,
146                 ));
147             }
148 
149             assert!(unwrap(obj_weak.is_same_object(&env, &obj_local), &env));
150 
151             assert!(
152                 !unwrap(obj_weak.is_garbage_collected(&env), &env),
153                 "`is_garbage_collected` returned incorrect value"
154             );
155         }
156 
157         assert!(unwrap(
158             obj_weak.is_weak_ref_to_same_object(&env, &obj_weak2),
159             &env,
160         ));
161 
162         drop(obj_local);
163         run_gc(&mut env);
164 
165         for obj_weak in &[&obj_weak, &obj_weak2] {
166             {
167                 let obj_local_from_weak = unwrap(obj_weak.upgrade_local(&env), &env);
168 
169                 assert!(
170                     obj_local_from_weak.is_none(),
171                     "object should have been GC'd"
172                 );
173             }
174 
175             {
176                 let obj_global_from_weak = unwrap(obj_weak.upgrade_global(&env), &env);
177 
178                 assert!(
179                     obj_global_from_weak.is_none(),
180                     "object should have been GC'd"
181                 );
182             }
183 
184             assert!(
185                 unwrap(obj_weak.is_garbage_collected(&env), &env),
186                 "`is_garbage_collected` returned incorrect value"
187             );
188         }
189 
190         assert!(unwrap(
191             obj_weak.is_weak_ref_to_same_object(&env, &obj_weak2),
192             &env,
193         ));
194     }
195 }
196