1/*
2 * Copyright (C) 2007 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
17package libcore.java.lang;
18
19import junit.framework.TestCase;
20
21public class OldAndroidMonitorTest extends TestCase {
22
23    public void testWaitArgumentsTest() throws Exception {
24            /* Try some valid arguments.  These should all
25             * return very quickly.
26             */
27            try {
28                synchronized (this) {
29                    /* millisecond version */
30                    wait(1);
31                    wait(10);
32
33                    /* millisecond + nanosecond version */
34                    wait(0, 1);
35                    wait(0, 999999);
36                    wait(1, 1);
37                    wait(1, 999999);
38                }
39            } catch (InterruptedException ex) {
40                throw new RuntimeException("good Object.wait() interrupted",
41                        ex);
42            } catch (Exception ex) {
43                throw new RuntimeException("Unexpected exception when calling" +
44                        "Object.wait() with good arguments", ex);
45            }
46
47            /* Try some invalid arguments.
48             */
49            boolean sawException = false;
50            try {
51                synchronized (this) {
52                    wait(-1);
53                }
54            } catch (InterruptedException ex) {
55                throw new RuntimeException("bad Object.wait() interrupted", ex);
56            } catch (IllegalArgumentException ex) {
57                sawException = true;
58            } catch (Exception ex) {
59                throw new RuntimeException("Unexpected exception when calling" +
60                        "Object.wait() with bad arguments", ex);
61            }
62            if (!sawException) {
63                throw new RuntimeException("bad call to Object.wait() should " +
64                        "have thrown IllegalArgumentException");
65            }
66
67            sawException = false;
68            try {
69                synchronized (this) {
70                    wait(0, -1);
71                }
72            } catch (InterruptedException ex) {
73                throw new RuntimeException("bad Object.wait() interrupted", ex);
74            } catch (IllegalArgumentException ex) {
75                sawException = true;
76            } catch (Exception ex) {
77                throw new RuntimeException("Unexpected exception when calling" +
78                        "Object.wait() with bad arguments", ex);
79            }
80            if (!sawException) {
81                throw new RuntimeException("bad call to Object.wait() should " +
82                        "have thrown IllegalArgumentException");
83            }
84
85            sawException = false;
86            try {
87                synchronized (this) {
88                    /* The legal range of nanos is 0-999999. */
89                    wait(0, 1000000);
90                }
91            } catch (InterruptedException ex) {
92                throw new RuntimeException("bad Object.wait() interrupted", ex);
93            } catch (IllegalArgumentException ex) {
94                sawException = true;
95            } catch (Exception ex) {
96                throw new RuntimeException("Unexpected exception when calling" +
97                        "Object.wait() with bad arguments", ex);
98            }
99            if (!sawException) {
100                throw new RuntimeException("bad call to Object.wait() should " +
101                        "have thrown IllegalArgumentException");
102            }
103    }
104
105    private class Interrupter extends Thread {
106            Waiter waiter;
107
108            Interrupter(String name, Waiter waiter) {
109                super(name);
110                this.waiter = waiter;
111            }
112
113            public void run() {
114                try {
115                    run_inner();
116                } catch (Throwable t) {
117                    OldAndroidMonitorTest.errorException = t;
118                    OldAndroidMonitorTest.testThread.interrupt();
119                }
120            }
121
122            void run_inner() {
123                waiter.spin = true;
124                // System.out.println("InterruptTest: starting waiter");
125                waiter.start();
126
127                try {
128                    Thread.currentThread().sleep(500);
129                } catch (InterruptedException ex) {
130                    throw new RuntimeException("Test sleep interrupted.", ex);
131                }
132
133                /* Waiter is spinning, and its monitor should still be thin.
134                 */
135                // System.out.println("Test interrupting waiter");
136                waiter.interrupt();
137                waiter.spin = false;
138
139                for (int i = 0; i < 3; i++) {
140                    /* Wait for the waiter to start waiting.
141                     */
142                    synchronized (waiter.interrupterLock) {
143                        try {
144                            waiter.interrupterLock.wait();
145                        } catch (InterruptedException ex) {
146                            throw new RuntimeException("Test wait interrupted.", ex);
147                        }
148                    }
149
150                    /* Before interrupting, grab the waiter lock, which
151                     * guarantees that the waiter is already sitting in wait().
152                     */
153                    synchronized (waiter) {
154                        //System.out.println("Test interrupting waiter (" + i + ")");
155                        waiter.interrupt();
156                    }
157                }
158
159                // System.out.println("Test waiting for waiter to die.");
160                try {
161                    waiter.join();
162                } catch (InterruptedException ex) {
163                    throw new RuntimeException("Test join interrupted.", ex);
164                }
165                // System.out.println("InterruptTest done.");
166            }
167        }
168
169    private class Waiter extends Thread {
170            Object interrupterLock = new Object();
171            Boolean spin = false;
172
173            Waiter(String name) {
174                super(name);
175            }
176
177            public void run() {
178                try {
179                    run_inner();
180                } catch (Throwable t) {
181                    OldAndroidMonitorTest.errorException = t;
182                    OldAndroidMonitorTest.testThread.interrupt();
183                }
184            }
185
186            void run_inner() {
187                // System.out.println("Waiter spinning");
188                while (spin) {
189                    // We're going to get interrupted while we spin.
190                }
191                if (interrupted()) {
192                    // System.out.println("Waiter done spinning; interrupted.");
193                } else {
194                    throw new RuntimeException("Thread not interrupted " +
195                                               "during spin");
196                }
197
198                synchronized (this) {
199                    Boolean sawEx = false;
200
201                    try {
202                        synchronized (interrupterLock) {
203                            interrupterLock.notify();
204                        }
205                        // System.out.println("Waiter calling wait()");
206                        this.wait();
207                    } catch (InterruptedException ex) {
208                        sawEx = true;
209                        // System.out.println("wait(): Waiter caught " + ex);
210                    }
211                    // System.out.println("wait() finished");
212
213                    if (!sawEx) {
214                        throw new RuntimeException("Thread not interrupted " +
215                                                   "during wait()");
216                    }
217                }
218                synchronized (this) {
219                    Boolean sawEx = false;
220
221                    try {
222                        synchronized (interrupterLock) {
223                            interrupterLock.notify();
224                        }
225                        // System.out.println("Waiter calling wait(1000)");
226                        this.wait(1000);
227                    } catch (InterruptedException ex) {
228                        sawEx = true;
229                        // System.out.println("wait(1000): Waiter caught " + ex);
230                    }
231                    // System.out.println("wait(1000) finished");
232
233                    if (!sawEx) {
234                        throw new RuntimeException("Thread not interrupted " +
235                                                   "during wait(1000)");
236                    }
237                }
238                synchronized (this) {
239                    Boolean sawEx = false;
240
241                    try {
242                        synchronized (interrupterLock) {
243                            interrupterLock.notify();
244                        }
245                        // System.out.println("Waiter calling wait(1000, 5000)");
246                        this.wait(1000, 5000);
247                    } catch (InterruptedException ex) {
248                        sawEx = true;
249                        // System.out.println("wait(1000, 5000): Waiter caught " + ex);
250                    }
251                    // System.out.println("wait(1000, 5000) finished");
252
253                    if (!sawEx) {
254                        throw new RuntimeException("Thread not interrupted " +
255                                                   "during wait(1000, 5000)");
256                    }
257                }
258
259               //  System.out.println("Waiter returning");
260            }
261        }
262
263    private static Throwable errorException;
264    private static Thread testThread;
265
266    // TODO: Flaky test. Add back MediumTest annotation once fixed
267    public void testInterruptTest() throws Exception {
268
269
270            testThread = Thread.currentThread();
271            errorException = null;
272
273            Waiter waiter = new Waiter("InterruptTest Waiter");
274            Interrupter interrupter =
275                    new Interrupter("InterruptTest Interrupter", waiter);
276            interrupter.start();
277
278            try {
279                interrupter.join();
280                waiter.join();
281            } catch (InterruptedException ex) {
282                throw new RuntimeException("Test join interrupted.", ex);
283            }
284
285            if (errorException != null) {
286                throw new RuntimeException("InterruptTest failed",
287                                           errorException);
288            }
289
290
291
292
293    }
294
295     private static void deepWait(int depth, Object lock) {
296            synchronized (lock) {
297                if (depth > 0) {
298                    deepWait(depth - 1, lock);
299                } else {
300                    String threadName = Thread.currentThread().getName();
301                    try {
302                        // System.out.println(threadName + " waiting");
303                        lock.wait();
304                        // System.out.println(threadName + " done waiting");
305                    } catch (InterruptedException ex) {
306                        // System.out.println(threadName + " interrupted.");
307                    }
308                }
309            }
310        }
311
312        private class Worker extends Thread {
313            Object lock;
314            int id;
315
316            Worker(int id, Object lock) {
317                super("Worker(" + id + ")");
318                this.id = id;
319                this.lock = lock;
320            }
321
322            public void run() {
323                int iterations = 0;
324
325                while (OldAndroidMonitorTest.running) {
326                    OldAndroidMonitorTest.deepWait(id, lock);
327                    iterations++;
328                }
329                // System.out.println(getName() + " done after " + iterations + " iterations.");
330            }
331        }
332
333    private static Object commonLock = new Object();
334        private static Boolean running = false;
335
336
337    public void testNestedMonitors() throws Exception {
338        final int NUM_WORKERS = 5;
339
340            Worker w[] = new Worker[NUM_WORKERS];
341            int i;
342
343            for (i = 0; i < NUM_WORKERS; i++) {
344                w[i] = new Worker(i * 2 - 1, new Object());
345            }
346
347            running = true;
348
349            // System.out.println("NestedMonitors: starting workers");
350            for (i = 0; i < NUM_WORKERS; i++) {
351                w[i].start();
352            }
353
354            try {
355                Thread.currentThread().sleep(1000);
356            } catch (InterruptedException ex) {
357               // System.out.println("Test sleep interrupted.");
358            }
359
360            for (i = 0; i < 100; i++) {
361                for (int j = 0; j < NUM_WORKERS; j++) {
362                    synchronized (w[j].lock) {
363                        w[j].lock.notify();
364                    }
365                }
366            }
367
368            // System.out.println("NesterMonitors: stopping workers");
369            running = false;
370            for (i = 0; i < NUM_WORKERS; i++) {
371                synchronized (w[i].lock) {
372                    w[i].lock.notifyAll();
373                }
374            }
375    }
376
377    private static class CompareAndExchange extends Thread {
378        static Object toggleLock = null;
379        static int toggle = -1;
380        static Boolean running = false;
381
382        public void run() {
383            toggleLock = new Object();
384            toggle = -1;
385
386            Worker w1 = new Worker(0, 1);
387            Worker w2 = new Worker(2, 3);
388            Worker w3 = new Worker(4, 5);
389            Worker w4 = new Worker(6, 7);
390
391            running = true;
392
393            // System.out.println("CompareAndExchange: starting workers");
394
395            w1.start();
396            w2.start();
397            w3.start();
398            w4.start();
399
400            try {
401                this.sleep(10000);
402            } catch (InterruptedException ex) {
403                // System.out.println(getName() + " interrupted.");
404            }
405
406            // System.out.println("MonitorTest: stopping workers");
407            running = false;
408
409            toggleLock = null;
410        }
411
412        class Worker extends Thread {
413            int i1;
414            int i2;
415
416            Worker(int i1, int i2) {
417                super("Worker(" + i1 + ", " + i2 + ")");
418                this.i1 = i1;
419                this.i2 = i2;
420            }
421
422            public void run() {
423                int iterations = 0;
424
425                /* Latch this because run() may set the static field to
426                 * null at some point.
427                 */
428                Object toggleLock = CompareAndExchange.toggleLock;
429
430                // System.out.println(getName() + " running");
431                try {
432                    while (CompareAndExchange.running) {
433                        synchronized (toggleLock) {
434                            int test;
435                            int check;
436
437                            if (CompareAndExchange.toggle == i1) {
438                                this.sleep(5 + i2);
439                                CompareAndExchange.toggle = test = i2;
440                            } else {
441                                this.sleep(5 + i1);
442                                CompareAndExchange.toggle = test = i1;
443                            }
444                            if ((check = CompareAndExchange.toggle) != test) {
445//                                System.out.println("Worker(" + i1 + ", " +
446//                                                   i2 + ") " + "test " + test +
447//                                                   " != toggle " + check);
448                                throw new RuntimeException(
449                                        "locked value changed");
450                            }
451                        }
452
453                        iterations++;
454                    }
455                } catch (InterruptedException ex) {
456                   // System.out.println(getName() + " interrupted.");
457                }
458
459//                System.out.println(getName() + " done after " +
460//                                   iterations + " iterations.");
461            }
462        }
463    }
464}
465