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.CountDownLatch;
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.List;
23 
24 public class Test923 {
run()25   public static void run() throws Exception {
26     doTest();
27   }
28 
doTest()29   private static void doTest() throws Exception {
30     // Start a watchdog, to make sure on deadlocks etc the test dies.
31     startWatchdog();
32 
33     sharedId = createRawMonitor();
34 
35     output = new ArrayList<String>(100);
36 
37     simpleTests(sharedId);
38 
39     for (String s : output) {
40       System.out.println(s);
41     }
42     output.clear();
43 
44     threadTests(sharedId);
45 
46     destroyRawMonitor(sharedId);
47   }
48 
simpleTests(long id)49   private static void simpleTests(long id) {
50     unlock(id);  // Should fail.
51 
52     lock(id);
53     unlock(id);
54     unlock(id);  // Should fail.
55 
56     lock(id);
57     lock(id);
58     unlock(id);
59     unlock(id);
60     unlock(id);  // Should fail.
61 
62     rawWait(id, 0);   // Should fail.
63     rawWait(id, -1);  // Should fail.
64     rawWait(id, 1);   // Should fail.
65 
66     lock(id);
67     rawWait(id, 50);
68     unlock(id);
69     unlock(id);  // Should fail.
70 
71     rawNotify(id);  // Should fail.
72     lock(id);
73     rawNotify(id);
74     unlock(id);
75     unlock(id);  // Should fail.
76 
77     rawNotifyAll(id);  // Should fail.
78     lock(id);
79     rawNotifyAll(id);
80     unlock(id);
81     unlock(id);  // Should fail.
82   }
83 
threadTests(final long id)84   private static void threadTests(final long id) throws Exception {
85     final int N = 10;
86 
87     final CountDownLatch waitLatch = new CountDownLatch(N);
88     final CountDownLatch wait2Latch = new CountDownLatch(1);
89 
90     Runnable r = new Runnable() {
91       @Override
92       public void run() {
93         lock(id);
94         waitLatch.countDown();
95         rawWait(id, 0);
96         firstAwakened = Thread.currentThread();
97         appendToLog("Awakened");
98         unlock(id);
99         wait2Latch.countDown();
100       }
101     };
102 
103     List<Thread> threads = new ArrayList<Thread>();
104     for (int i = 0; i < N; i++) {
105       Thread t = new Thread(r);
106       threads.add(t);
107       t.start();
108     }
109 
110     // Wait till all threads have been started.
111     waitLatch.await();
112 
113     // Hopefully enough time for all the threads to progress into wait.
114     Thread.yield();
115     Thread.sleep(500);
116 
117     // Wake up one.
118     lock(id);
119     rawNotify(id);
120     unlock(id);
121 
122     wait2Latch.await();
123 
124     // Wait a little bit more to see stragglers. This is flaky - spurious wakeups could
125     // make the test fail.
126     Thread.yield();
127     Thread.sleep(500);
128     if (firstAwakened != null) {
129       firstAwakened.join();
130     }
131 
132     // Wake up everyone else.
133     lock(id);
134     rawNotifyAll(id);
135     unlock(id);
136 
137     // Wait for everyone to die.
138     for (Thread t : threads) {
139       t.join();
140     }
141 
142     // Check threaded output.
143     Iterator<String> it = output.iterator();
144     // 1) Start with N locks and Waits.
145     {
146       int locks = 0;
147       int waits = 0;
148       for (int i = 0; i < 2*N; i++) {
149         String s = it.next();
150         if (s.equals("Lock")) {
151           locks++;
152         } else if (s.equals("Wait")) {
153           if (locks <= waits) {
154             System.out.println(output);
155             throw new RuntimeException("Wait before Lock");
156           }
157           waits++;
158         } else {
159           System.out.println(output);
160           throw new RuntimeException("Unexpected operation: " + s);
161         }
162       }
163     }
164 
165     // 2) Expect Lock + Notify + Unlock.
166     expect("Lock", it, output);
167     expect("Notify", it, output);
168     expect("Unlock", it, output);
169 
170     // 3) A single thread wakes up, runs, and dies.
171     expect("Awakened", it, output);
172     expect("Unlock", it, output);
173 
174     // 4) Expect Lock + NotifyAll + Unlock.
175     expect("Lock", it, output);
176     expect("NotifyAll", it, output);
177     expect("Unlock", it, output);
178 
179     // 5) N-1 threads wake up, run, and die.
180     {
181       int expectedUnlocks = 0;
182       int ops = 2 * (N-1);
183       for (int i = 0; i < ops; i++) {
184         String s = it.next();
185         if (s.equals("Awakened")) {
186           expectedUnlocks++;
187         } else if (s.equals("Unlock")) {
188           expectedUnlocks--;
189           if (expectedUnlocks < 0) {
190             System.out.println(output);
191             throw new RuntimeException("Unexpected unlock");
192           }
193         }
194       }
195     }
196 
197     // 6) That should be it.
198     if (it.hasNext()) {
199       System.out.println(output);
200       throw new RuntimeException("Unexpected trailing output, starting with " + it.next());
201     }
202 
203     output.clear();
204     System.out.println("Done");
205   }
206 
expect(String s, Iterator<String> it, List<String> output)207   private static void expect(String s, Iterator<String> it, List<String> output) {
208     String t = it.next();
209     if (!s.equals(t)) {
210       System.out.println(output);
211       throw new RuntimeException("Expected " + s + " but got " + t);
212     }
213   }
214 
lock(long id)215   private static void lock(long id) {
216     appendToLog("Lock");
217     rawMonitorEnter(id);
218   }
219 
unlock(long id)220   private static void unlock(long id) {
221     appendToLog("Unlock");
222     try {
223       rawMonitorExit(id);
224     } catch (RuntimeException e) {
225       appendToLog(e.getMessage());
226     }
227   }
228 
rawWait(long id, long millis)229   private static void rawWait(long id, long millis) {
230     appendToLog("Wait");
231     try {
232       rawMonitorWait(id, millis);
233     } catch (RuntimeException e) {
234       appendToLog(e.getMessage());
235     }
236   }
237 
rawNotify(long id)238   private static void rawNotify(long id) {
239     appendToLog("Notify");
240     try {
241       rawMonitorNotify(id);
242     } catch (RuntimeException e) {
243       appendToLog(e.getMessage());
244     }
245   }
246 
rawNotifyAll(long id)247   private static void rawNotifyAll(long id) {
248     appendToLog("NotifyAll");
249     try {
250       rawMonitorNotifyAll(id);
251     } catch (RuntimeException e) {
252       appendToLog(e.getMessage());
253     }
254   }
255 
appendToLog(String s)256   private static synchronized void appendToLog(String s) {
257     output.add(s);
258   }
259 
startWatchdog()260   private static void startWatchdog() {
261     Runnable r = new Runnable() {
262       @Override
263       public void run() {
264         long start = System.currentTimeMillis();
265         // Give it a minute.
266         long end = 60 * 1000 + start;
267         for (;;) {
268           long delta = end - System.currentTimeMillis();
269           if (delta <= 0) {
270             break;
271           }
272 
273           try {
274             Thread.currentThread().sleep(delta);
275           } catch (Exception e) {
276           }
277         }
278         System.out.println("TIMEOUT!");
279         System.exit(1);
280       }
281     };
282     Thread t = new Thread(r);
283     t.setDaemon(true);
284     t.start();
285   }
286 
287   static volatile long sharedId;
288   static List<String> output;
289   static Thread firstAwakened;
290 
createRawMonitor()291   private static native long createRawMonitor();
destroyRawMonitor(long id)292   private static native void destroyRawMonitor(long id);
rawMonitorEnter(long id)293   private static native void rawMonitorEnter(long id);
rawMonitorExit(long id)294   private static native void rawMonitorExit(long id);
rawMonitorWait(long id, long millis)295   private static native void rawMonitorWait(long id, long millis);
rawMonitorNotify(long id)296   private static native void rawMonitorNotify(long id);
rawMonitorNotifyAll(long id)297   private static native void rawMonitorNotifyAll(long id);
298 }
299