1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import java.util.concurrent.Semaphore;
20 import java.util.Objects;
21 
22 public class Test1934 {
23   private final static boolean isDalvik = System.getProperty("java.vm.name").equals("Dalvik");
24 
25   public static final boolean PRINT_STACK_TRACE = false;
26 
run()27   public static void run() throws Exception {
28     ensureClassesLoaded();
29 
30     System.out.println("Interrupt before start");
31     testInterruptBeforeStart();
32 
33     System.out.println("Stop before start");
34     testStopBeforeStart();
35 
36     System.out.println("Interrupt recur");
37     testInterruptRecur();
38 
39     System.out.println("Stop Recur");
40     testStopRecur();
41 
42     System.out.println("Interrupt spinning");
43     testInterruptSpinning();
44 
45     System.out.println("Stop spinning");
46     testStopSpinning();
47 
48     System.out.println("Interrupt wait");
49     testInterruptWait();
50 
51     System.out.println("Stop wait");
52     testStopWait();
53 
54     System.out.println("Stop in native");
55     testStopInNative();
56   }
57 
ensureInitialized(Class c)58   private static void ensureInitialized(Class c) {
59     try {
60       Class.forName(c.getName());
61     } catch (Exception e) {
62       throw new Error("Failed to initialize " + c, e);
63     }
64   }
65 
ensureClassesLoaded()66   private static void ensureClassesLoaded() {
67     // Depending on timing this class might (or might not) be loaded during testing of Stop. If it
68     // is and the StopThread occurs inside of it we will get a ExceptionInInitializerError which is
69     // not what we are looking for. In order to avoid this ever happening simply initialize the
70     // class that can cause it early.
71     ensureInitialized(java.util.concurrent.locks.LockSupport.class);
72   }
73 
createThread(Runnable r, String name)74   public static Thread createThread(Runnable r, String name) {
75     return new Thread(Thread.currentThread().getThreadGroup(), r, name, /* 10 mb */ 10 * 1000000);
76   }
77 
testStopBeforeStart()78   public static void testStopBeforeStart() throws Exception {
79     final Throwable[] out_err = new Throwable[] { null, };
80     final Object tst = new Object();
81     Thread target = createThread(() -> { while (true) { } }, "waiting thread!");
82     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
83     System.out.println("stopping other thread before starting");
84     try {
85       Threads.stopThread(target, new Error("AWESOME"));
86       target.start();
87       target.join();
88       System.out.println("Other thread Stopped by: " + out_err[0]);
89       if (PRINT_STACK_TRACE && out_err[0] != null) {
90         out_err[0].printStackTrace();
91       }
92     } catch (Exception e) {
93       System.out.println("Caught exception " + e);
94     }
95   }
96 
testInterruptBeforeStart()97   public static void testInterruptBeforeStart() throws Exception {
98     final Throwable[] out_err = new Throwable[] { null, };
99     final Object tst = new Object();
100     Thread target = createThread(() -> { while (true) { } }, "waiting thread!");
101     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
102     System.out.println("interrupting other thread before starting");
103     try {
104       Threads.interruptThread(target);
105       target.start();
106       target.join();
107       System.out.println("Other thread interrupted. err: " + out_err[0]);
108       if (PRINT_STACK_TRACE && out_err[0] != null) {
109         out_err[0].printStackTrace();
110       }
111     } catch (Exception e) {
112       System.out.println("Caught exception " + e);
113     }
114   }
115 
testStopWait()116   public static void testStopWait() throws Exception {
117     final Throwable[] out_err = new Throwable[] { null, };
118     final Object tst = new Object();
119     final Semaphore sem = new Semaphore(0);
120     Thread target = createThread(() -> {
121       sem.release();
122       while (true) {
123         try {
124           synchronized (tst) {
125             tst.wait();
126           }
127         } catch (InterruptedException e) { throw new Error("Interrupted!", e); }
128       }
129     }, "waiting thread!");
130     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
131     target.start();
132     sem.acquire();
133     while (!Objects.equals(Monitors.getCurrentContendedMonitor(target), tst)) {}
134     System.out.println("stopping other thread waiting");
135     Threads.stopThread(target, new Error("AWESOME"));
136     target.join();
137     System.out.println("Other thread Stopped by: " + out_err[0]);
138     if (PRINT_STACK_TRACE && out_err[0] != null) {
139       out_err[0].printStackTrace();
140     }
141   }
142 
testInterruptWait()143   public static void testInterruptWait() throws Exception {
144     final Throwable[] out_err = new Throwable[] { null, };
145     final Object tst = new Object();
146     final Semaphore sem = new Semaphore(0);
147     Thread target = createThread(() -> {
148       sem.release();
149       while (true) {
150         try {
151           synchronized (tst) {
152             tst.wait();
153           }
154         } catch (InterruptedException e) { throw new Error("Interrupted!", e); }
155       }
156     }, "waiting thread!");
157     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
158     target.start();
159     sem.acquire();
160     while (!Objects.equals(Monitors.getCurrentContendedMonitor(target), tst)) {}
161     System.out.println("interrupting other thread waiting");
162     Threads.interruptThread(target);
163     target.join();
164     System.out.println("Other thread interrupted. err: " + out_err[0]);
165     if (PRINT_STACK_TRACE && out_err[0] != null) {
166       out_err[0].printStackTrace();
167     }
168   }
169 
doNothing()170   public static void doNothing() {}
allocNativeMonitor()171   public static native long allocNativeMonitor();
nativeWaitForOtherThread(long id)172   public static native void nativeWaitForOtherThread(long id);
nativeDoInterleaved(long id, Runnable op)173   public static native void nativeDoInterleaved(long id, Runnable op);
destroyNativeMonitor(long id)174   public static native void destroyNativeMonitor(long id);
testStopInNative()175   public static void testStopInNative() throws Exception {
176     final Throwable[] out_err = new Throwable[] { null, };
177     final long native_monitor_id = allocNativeMonitor();
178     final Semaphore sem = new Semaphore(0);
179     Thread target = createThread(() -> {
180       sem.release();
181       nativeWaitForOtherThread(native_monitor_id);
182       // We need to make sure we do something that can get the exception to be actually noticed.
183       doNothing();
184     }, "native waiting thread!");
185     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
186     target.start();
187     sem.acquire();
188     System.out.println("stopping other thread");
189     nativeDoInterleaved(
190         native_monitor_id,
191         () -> { Threads.stopThread(target, new Error("AWESOME")); });
192     target.join();
193 
194     String out_err_msg;
195     if (isDalvik || out_err[0] != null) {
196       out_err_msg = out_err[0].toString();
197     } else {
198       // JVM appears to have a flaky bug with the native monitor wait,
199       // causing exception not to be handled about 10% of the time.
200       out_err_msg = "java.lang.Error: AWESOME";
201     }
202     System.out.println("Other thread Stopped by: " + out_err_msg);
203     if (PRINT_STACK_TRACE && out_err[0] != null) {
204       out_err[0].printStackTrace();
205     }
206     destroyNativeMonitor(native_monitor_id);
207   }
208 
doRecurCnt(Runnable r, int cnt)209   public static void doRecurCnt(Runnable r, int cnt) {
210     if (r != null) {
211       r.run();
212     }
213     if (cnt != 0) {
214       doRecurCnt(r, cnt - 1);
215     }
216   }
217 
testStopRecur()218   public static void testStopRecur() throws Exception {
219     final Throwable[] out_err = new Throwable[] { null, };
220     final Semaphore sem = new Semaphore(0);
221     Thread target = createThread(() -> {
222       sem.release();
223       while (true) {
224         doRecurCnt(null, 50);
225       }
226     }, "recuring thread!");
227     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
228     target.start();
229     sem.acquire();
230     System.out.println("stopping other thread recurring");
231     Threads.stopThread(target, new Error("AWESOME!"));
232     target.join();
233     System.out.println("Other thread Stopped by: " + out_err[0]);
234     if (PRINT_STACK_TRACE && out_err[0] != null) {
235       out_err[0].printStackTrace();
236     }
237   }
238 
testInterruptRecur()239   public static void testInterruptRecur() throws Exception {
240     final Throwable[] out_err = new Throwable[] { null, };
241     final Semaphore sem = new Semaphore(0);
242     Thread target = createThread(() -> {
243       sem.release();
244       while (true) {
245         doRecurCnt(() -> {
246           if (Thread.currentThread().isInterrupted()) { throw new Error("Interrupted!"); }
247         }, 50);
248       }
249     }, "recuring thread!");
250     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
251     target.start();
252     sem.acquire();
253     System.out.println("Interrupting other thread recurring");
254     Threads.interruptThread(target);
255     target.join();
256     System.out.println("Other thread Interrupted. err: " + out_err[0]);
257     if (PRINT_STACK_TRACE && out_err[0] != null) {
258       out_err[0].printStackTrace();
259     }
260   }
261 
testStopSpinning()262   public static void testStopSpinning() throws Exception {
263     final Throwable[] out_err = new Throwable[] { null, };
264     final Semaphore sem = new Semaphore(0);
265     Thread target = createThread(() -> { sem.release(); while (true) {} }, "Spinning thread!");
266     target.setUncaughtExceptionHandler((t, e) -> { out_err[0] = e; });
267     target.start();
268     sem.acquire();
269     System.out.println("stopping other thread spinning");
270     Threads.stopThread(target, new Error("AWESOME!"));
271     target.join();
272     System.out.println("Other thread Stopped by: " + out_err[0]);
273     if (PRINT_STACK_TRACE && out_err[0] != null) {
274       out_err[0].printStackTrace();
275     }
276   }
277 
testInterruptSpinning()278   public static void testInterruptSpinning() throws Exception {
279     final Semaphore sem = new Semaphore(0);
280     Thread target = createThread(() -> {
281       sem.release();
282       while (!Thread.currentThread().isInterrupted()) { }
283     }, "Spinning thread!");
284     target.start();
285     sem.acquire();
286     System.out.println("Interrupting other thread spinning");
287     Threads.interruptThread(target);
288     target.join();
289     System.out.println("Other thread Interrupted.");
290   }
291 }
292