1/*
2 * Copyright (C) 2013 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
17import java.lang.reflect.*;
18import java.lang.Runtime;
19
20public class Main {
21    static Object nativeLock = new Object();
22    static Object deadlockLock = new Object();
23    static boolean aboutToDeadlockLock = false;
24    static int nativeBytes = 0;
25    static Object runtime;
26    static Method register_native_allocation;
27    static Method register_native_free;
28    static long maxMem = 0;
29
30    static class NativeAllocation {
31        private int bytes;
32
33        NativeAllocation(int bytes, boolean testingDeadlock) throws Exception {
34            this.bytes = bytes;
35            register_native_allocation.invoke(runtime, bytes);
36            synchronized (nativeLock) {
37                if (!testingDeadlock) {
38                    nativeBytes += bytes;
39                    if (nativeBytes > maxMem) {
40                        throw new OutOfMemoryError();
41                    }
42                }
43            }
44        }
45
46        protected void finalize() throws Exception {
47            synchronized (nativeLock) {
48                nativeBytes -= bytes;
49            }
50            register_native_free.invoke(runtime, bytes);
51            aboutToDeadlockLock = true;
52            synchronized (deadlockLock) {
53            }
54        }
55    }
56
57    public static void main(String[] args) throws Exception {
58        Class<?> vm_runtime = Class.forName("dalvik.system.VMRuntime");
59        Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
60        runtime = get_runtime.invoke(null);
61        register_native_allocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
62        register_native_free = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
63        maxMem = Runtime.getRuntime().maxMemory();
64        int count = 16;
65        int size = (int)(maxMem / 2 / count);
66        int allocation_count = 256;
67        NativeAllocation[] allocations = new NativeAllocation[count];
68        for (int i = 0; i < allocation_count; ++i) {
69            allocations[i % count] = new NativeAllocation(size, false);
70        }
71        // Test that we don't get a deadlock if we are holding nativeLock. If there is no timeout,
72        // then we will get a finalizer timeout exception.
73        aboutToDeadlockLock = false;
74        synchronized (deadlockLock) {
75            for (int i = 0; aboutToDeadlockLock != true; ++i) {
76                allocations[i % count] = new NativeAllocation(size, true);
77            }
78            // Do more allocations now that the finalizer thread is deadlocked so that we force
79            // finalization and timeout.
80            for (int i = 0; i < 10; ++i) {
81                allocations[i % count] = new NativeAllocation(size, true);
82            }
83        }
84        System.out.println("Test complete");
85    }
86}
87
88