1/*
2 * Copyright (C) 2016 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 art;
18
19import java.lang.ref.WeakReference;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.concurrent.CountDownLatch;
23import java.util.concurrent.Semaphore;
24
25public class Test1900 {
26  public static void checkLE(long exp, long o) {
27    if (exp > o) {
28      throw new Error("Expected: " + exp + " Got: " + o);
29    }
30  }
31  public static void checkEq(long exp, long o) {
32    if (exp != o) {
33      throw new Error("Expected: " + exp + " Got: " + o);
34    }
35  }
36
37  public static void runConcurrent(Runnable... rs) throws Exception {
38    final CountDownLatch latch = new CountDownLatch(rs.length);
39    Thread[] thrs = new Thread[rs.length];
40    for (int i = 0; i < rs.length; i++) {
41      final Runnable r = rs[i];
42      thrs[i] = new Thread(() -> {
43        latch.countDown();
44        r.run();
45      });
46      thrs[i].start();
47    }
48    for (Thread thr : thrs) {
49      thr.join();
50    }
51  }
52  static class Holder {
53    public long val;
54  }
55
56  public static void run() throws Exception {
57    initializeTest();
58    // Get the overhead for the native part of this test.
59    final long base_state = getAmountAllocated();
60
61    // Basic alloc-dealloc
62    checkEq(base_state + 0, getAmountAllocated());
63    long abc = doAllocate(10);
64    checkLE(base_state + 10, getAmountAllocated());
65    long def = doAllocate(10);
66    checkLE(base_state + 20, getAmountAllocated());
67    doDeallocate(abc);
68    checkLE(base_state + 10, getAmountAllocated());
69
70    doDeallocate(def);
71
72    checkEq(base_state + 0, getAmountAllocated());
73
74    // Try doing it concurrently.
75    Runnable add10 = () -> { long x = doAllocate(10); doDeallocate(x); };
76    Runnable[] rs = new Runnable[100];
77    Arrays.fill(rs, add10);
78    runConcurrent(rs);
79    checkEq(base_state + 0, getAmountAllocated());
80
81    // Try doing it concurrently with different threads to allocate and deallocate.
82    final Semaphore sem = new Semaphore(0);
83    final Holder h = new Holder();
84    runConcurrent(
85        () -> {
86          try {
87            h.val = doAllocate(100);
88            checkLE(base_state + 100, getAmountAllocated());
89            sem.release();
90          } catch (Exception e) { throw new Error("exception!", e); }
91        },
92        () -> {
93          try {
94            sem.acquire();
95            long after_acq = getAmountAllocated();
96            doDeallocate(h.val);
97            checkLE(base_state + 100, after_acq);
98          } catch (Exception e) { throw new Error("exception!", e); }
99        }
100    );
101    checkEq(base_state + 0, getAmountAllocated());
102
103    // Try doing it with multiple jvmtienvs.
104    long env1 = newJvmtiEnv();
105    long env2 = newJvmtiEnv();
106
107    final long new_base_state = getAmountAllocated();
108    // new jvmtienvs shouldn't save us memory.
109    checkLE(base_state, new_base_state);
110    // Make sure we track both.
111    abc = doAllocate(env1, 10);
112    checkLE(new_base_state + 10, getAmountAllocated());
113    def = doAllocate(env2, 10);
114    checkLE(new_base_state + 20, getAmountAllocated());
115    doDeallocate(env1, abc);
116    checkLE(new_base_state + 10, getAmountAllocated());
117
118    doDeallocate(env2, def);
119
120    checkEq(new_base_state + 0, getAmountAllocated());
121
122    destroyJvmtiEnv(env1);
123    destroyJvmtiEnv(env2);
124
125    // Back to normal after getting rid of the envs.
126    checkEq(base_state + 0, getAmountAllocated());
127
128    // Try adding some tags
129    Object a = new Object();
130    Object b = new Object();
131    Main.setTag(a, 100);
132    Main.setTag(b, 200);
133
134    // tags should be counted and should have some data associated with them.
135    checkLE(base_state + 1, getAmountAllocated());
136  }
137
138  private static native long doAllocate(long jvmtienv, long size);
139  private static long doAllocate(long size) {
140    return doAllocate(getDefaultJvmtiEnv(), size);
141  }
142
143  private static native void doDeallocate(long jvmtienv, long ptr);
144  private static void doDeallocate(long size) {
145    doDeallocate(getDefaultJvmtiEnv(), size);
146  }
147
148  private static native long getDefaultJvmtiEnv();
149  private static native long newJvmtiEnv();
150  private static native void destroyJvmtiEnv(long jvmtienv);
151  private static native long getAmountAllocated();
152  private static native void initializeTest();
153}
154