xref: /aosp_15_r20/art/test/165-lock-owner-proxy/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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 import java.lang.reflect.InvocationHandler;
18 import java.lang.reflect.Method;
19 import java.lang.reflect.Proxy;
20 
21 // Note that this is run with a tiny heap.
22 // See b/260228356 for some discussion. It's unclear if and how this reliably forces an
23 // OOME. For now, we're keeping this around because it appears to have detected some bugs
24 // in the past. It may need revisiting.
25 
26 public class Main {
27     static final int numberOfThreads = 5;
28     static final int totalOperations = 10000;
29 
30     final static Object lockObject = new Object();
31     static SimpleInterface inf;
32     static volatile boolean finish = false;
33 
main(String[] args)34     public static void main(String[] args) throws Exception {
35         // Wait for system daemons to start, so that we minimize the chances of
36         // a system daemon still starting up if/when we run out of memory.
37         try {
38             Thread.sleep(100);
39         } catch (InterruptedException e) {
40             System.out.println("Unexpected interrupt:" + e);
41         }
42 
43         inf = (SimpleInterface)Proxy.newProxyInstance(SimpleInterface.class.getClassLoader(),
44             new Class[] { SimpleInterface.class }, new EmptyInvocationHandler());
45         Thread garbageThread = new Thread(new GarbageRunner());
46         garbageThread.start();
47 
48         final Thread[] threads = new Thread[numberOfThreads];
49         for (int t = 0; t < threads.length; t++) {
50             threads[t] = new Thread((t % 2 == 0) ? new ProxyRunner() : new SyncRunner());
51         }
52         for (Thread t : threads) {
53             t.start();
54         }
55 
56         // Now wait.
57         for (Thread t : threads) {
58             t.join();
59         }
60         finish = true;
61         garbageThread.join();
62     }
63 
64     private static interface SimpleInterface {
65         // Add some primitives to force some allocation when calling.
foo(int i1, int i2, int i3, int i4, int i5, int i6)66         public void foo(int i1, int i2, int i3, int i4, int i5, int i6);
67     }
68 
69     private static class EmptyInvocationHandler implements InvocationHandler {
invoke(Object proxy, Method method, Object[] args)70         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
71             return null;
72         }
73     }
74 
75     private static class ProxyRunner implements Runnable {
run()76         public void run() {
77             int count = totalOperations;
78             while (count > 0) {
79                 synchronized (lockObject) {
80                     try {
81                         inf.foo(10000 - count, 11000 - count, 12000 - count, 13000 - count,
82                                 14000 - count, 15000 - count);
83                     } catch (OutOfMemoryError e) {
84                         // Ignore errors. This is the test for b/69121347 - see an exception
85                         // instead of native abort.
86                     }
87               }
88               count--;
89             }
90         }
91     }
92 
93     private static class SyncRunner implements Runnable {
run()94         public void run() {
95             int count = totalOperations;
96             while (count > 0) {
97                 synchronized (lockObject) {
98                     // "Wait" a small amount of time.
99                     long start = System.nanoTime();
100                     long delta = 10 * 1000;  // 10 us.
101                     long elapsed;
102                     do {
103                       elapsed = System.nanoTime();
104                     } while (elapsed - start < delta);
105                 }
106                 count--;
107             }
108         }
109     }
110 
111     private static class GarbageRunner implements Runnable {
run()112         public void run() {
113             while (!finish) {
114                 // Some random allocations adding up to almost 2M.
115                 for (int i = 0; i < 188; i++) {
116                     try {
117                         byte b[] = new byte[i * 100 + 10];
118                     } catch (OutOfMemoryError e) {
119                         // Ignore. This is just to improve chances that an OOME is thrown during
120                         // proxy invocation.
121                     }
122                 }
123                 try {
124                     Thread.sleep(10);
125                 } catch (Exception e) {
126                     System.out.println("Unexpected exception in sleep():" + e);
127                 }
128             }
129         }
130     }
131 }
132