indirect_reference_table_test.cc revision 28bd2e4f151267b34b8e1eb19c489d8d547bbf5c
1/*
2 * Copyright (C) 2009 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#include "indirect_reference_table-inl.h"
18
19#include "class_linker-inl.h"
20#include "common_runtime_test.h"
21#include "mirror/object-inl.h"
22#include "scoped_thread_state_change-inl.h"
23
24namespace art {
25
26class IndirectReferenceTableTest : public CommonRuntimeTest {};
27
28static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique)
29    REQUIRES_SHARED(Locks::mutator_lock_) {
30  std::ostringstream oss;
31  irt->Dump(oss);
32  if (num_objects == 0) {
33    EXPECT_EQ(oss.str().find("java.lang.Object"), std::string::npos) << oss.str();
34  } else if (num_objects == 1) {
35    EXPECT_NE(oss.str().find("1 of java.lang.Object"), std::string::npos) << oss.str();
36  } else {
37    EXPECT_NE(oss.str().find(StringPrintf("%zd of java.lang.Object (%zd unique instances)",
38                                          num_objects, num_unique)),
39              std::string::npos)
40                  << "\n Expected number of objects: " << num_objects
41                  << "\n Expected unique objects: " << num_unique << "\n"
42                  << oss.str();
43  }
44}
45
46TEST_F(IndirectReferenceTableTest, BasicTest) {
47  // This will lead to error messages in the log.
48  ScopedLogSeverity sls(LogSeverity::FATAL);
49
50  ScopedObjectAccess soa(Thread::Current());
51  static const size_t kTableInitial = 10;
52  static const size_t kTableMax = 20;
53  IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal);
54
55  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
56  StackHandleScope<4> hs(soa.Self());
57  ASSERT_TRUE(c != nullptr);
58  Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
59  ASSERT_TRUE(obj0.Get() != nullptr);
60  Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
61  ASSERT_TRUE(obj1.Get() != nullptr);
62  Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
63  ASSERT_TRUE(obj2.Get() != nullptr);
64  Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
65  ASSERT_TRUE(obj3.Get() != nullptr);
66
67  const uint32_t cookie = IRT_FIRST_SEGMENT;
68
69  CheckDump(&irt, 0, 0);
70
71  IndirectRef iref0 = (IndirectRef) 0x11110;
72  EXPECT_FALSE(irt.Remove(cookie, iref0)) << "unexpectedly successful removal";
73
74  // Add three, check, remove in the order in which they were added.
75  iref0 = irt.Add(cookie, obj0.Get());
76  EXPECT_TRUE(iref0 != nullptr);
77  CheckDump(&irt, 1, 1);
78  IndirectRef iref1 = irt.Add(cookie, obj1.Get());
79  EXPECT_TRUE(iref1 != nullptr);
80  CheckDump(&irt, 2, 2);
81  IndirectRef iref2 = irt.Add(cookie, obj2.Get());
82  EXPECT_TRUE(iref2 != nullptr);
83  CheckDump(&irt, 3, 3);
84
85  EXPECT_OBJ_PTR_EQ(obj0.Get(), irt.Get(iref0));
86  EXPECT_OBJ_PTR_EQ(obj1.Get(), irt.Get(iref1));
87  EXPECT_OBJ_PTR_EQ(obj2.Get(), irt.Get(iref2));
88
89  EXPECT_TRUE(irt.Remove(cookie, iref0));
90  CheckDump(&irt, 2, 2);
91  EXPECT_TRUE(irt.Remove(cookie, iref1));
92  CheckDump(&irt, 1, 1);
93  EXPECT_TRUE(irt.Remove(cookie, iref2));
94  CheckDump(&irt, 0, 0);
95
96  // Table should be empty now.
97  EXPECT_EQ(0U, irt.Capacity());
98
99  // Get invalid entry (off the end of the list).
100  EXPECT_TRUE(irt.Get(iref0) == nullptr);
101
102  // Add three, remove in the opposite order.
103  iref0 = irt.Add(cookie, obj0.Get());
104  EXPECT_TRUE(iref0 != nullptr);
105  iref1 = irt.Add(cookie, obj1.Get());
106  EXPECT_TRUE(iref1 != nullptr);
107  iref2 = irt.Add(cookie, obj2.Get());
108  EXPECT_TRUE(iref2 != nullptr);
109  CheckDump(&irt, 3, 3);
110
111  ASSERT_TRUE(irt.Remove(cookie, iref2));
112  CheckDump(&irt, 2, 2);
113  ASSERT_TRUE(irt.Remove(cookie, iref1));
114  CheckDump(&irt, 1, 1);
115  ASSERT_TRUE(irt.Remove(cookie, iref0));
116  CheckDump(&irt, 0, 0);
117
118  // Table should be empty now.
119  ASSERT_EQ(0U, irt.Capacity());
120
121  // Add three, remove middle / middle / bottom / top.  (Second attempt
122  // to remove middle should fail.)
123  iref0 = irt.Add(cookie, obj0.Get());
124  EXPECT_TRUE(iref0 != nullptr);
125  iref1 = irt.Add(cookie, obj1.Get());
126  EXPECT_TRUE(iref1 != nullptr);
127  iref2 = irt.Add(cookie, obj2.Get());
128  EXPECT_TRUE(iref2 != nullptr);
129  CheckDump(&irt, 3, 3);
130
131  ASSERT_EQ(3U, irt.Capacity());
132
133  ASSERT_TRUE(irt.Remove(cookie, iref1));
134  CheckDump(&irt, 2, 2);
135  ASSERT_FALSE(irt.Remove(cookie, iref1));
136  CheckDump(&irt, 2, 2);
137
138  // Get invalid entry (from hole).
139  EXPECT_TRUE(irt.Get(iref1) == nullptr);
140
141  ASSERT_TRUE(irt.Remove(cookie, iref2));
142  CheckDump(&irt, 1, 1);
143  ASSERT_TRUE(irt.Remove(cookie, iref0));
144  CheckDump(&irt, 0, 0);
145
146  // Table should be empty now.
147  ASSERT_EQ(0U, irt.Capacity());
148
149  // Add four entries.  Remove #1, add new entry, verify that table size
150  // is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
151  // that we delete one and don't hole-compact the other.
152  iref0 = irt.Add(cookie, obj0.Get());
153  EXPECT_TRUE(iref0 != nullptr);
154  iref1 = irt.Add(cookie, obj1.Get());
155  EXPECT_TRUE(iref1 != nullptr);
156  iref2 = irt.Add(cookie, obj2.Get());
157  EXPECT_TRUE(iref2 != nullptr);
158  IndirectRef iref3 = irt.Add(cookie, obj3.Get());
159  EXPECT_TRUE(iref3 != nullptr);
160  CheckDump(&irt, 4, 4);
161
162  ASSERT_TRUE(irt.Remove(cookie, iref1));
163  CheckDump(&irt, 3, 3);
164
165  iref1 = irt.Add(cookie, obj1.Get());
166  EXPECT_TRUE(iref1 != nullptr);
167
168  ASSERT_EQ(4U, irt.Capacity()) << "hole not filled";
169  CheckDump(&irt, 4, 4);
170
171  ASSERT_TRUE(irt.Remove(cookie, iref1));
172  CheckDump(&irt, 3, 3);
173  ASSERT_TRUE(irt.Remove(cookie, iref3));
174  CheckDump(&irt, 2, 2);
175
176  ASSERT_EQ(3U, irt.Capacity()) << "should be 3 after two deletions";
177
178  ASSERT_TRUE(irt.Remove(cookie, iref2));
179  CheckDump(&irt, 1, 1);
180  ASSERT_TRUE(irt.Remove(cookie, iref0));
181  CheckDump(&irt, 0, 0);
182
183  ASSERT_EQ(0U, irt.Capacity()) << "not empty after split remove";
184
185  // Add an entry, remove it, add a new entry, and try to use the original
186  // iref.  They have the same slot number but are for different objects.
187  // With the extended checks in place, this should fail.
188  iref0 = irt.Add(cookie, obj0.Get());
189  EXPECT_TRUE(iref0 != nullptr);
190  CheckDump(&irt, 1, 1);
191  ASSERT_TRUE(irt.Remove(cookie, iref0));
192  CheckDump(&irt, 0, 0);
193  iref1 = irt.Add(cookie, obj1.Get());
194  EXPECT_TRUE(iref1 != nullptr);
195  CheckDump(&irt, 1, 1);
196  ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded";
197  CheckDump(&irt, 1, 1);
198  ASSERT_TRUE(irt.Remove(cookie, iref1)) << "switched del failed";
199  ASSERT_EQ(0U, irt.Capacity()) << "switching del not empty";
200  CheckDump(&irt, 0, 0);
201
202  // Same as above, but with the same object.  A more rigorous checker
203  // (e.g. with slot serialization) will catch this.
204  iref0 = irt.Add(cookie, obj0.Get());
205  EXPECT_TRUE(iref0 != nullptr);
206  CheckDump(&irt, 1, 1);
207  ASSERT_TRUE(irt.Remove(cookie, iref0));
208  CheckDump(&irt, 0, 0);
209  iref1 = irt.Add(cookie, obj0.Get());
210  EXPECT_TRUE(iref1 != nullptr);
211  CheckDump(&irt, 1, 1);
212  if (iref0 != iref1) {
213    // Try 0, should not work.
214    ASSERT_FALSE(irt.Remove(cookie, iref0)) << "temporal del succeeded";
215  }
216  ASSERT_TRUE(irt.Remove(cookie, iref1)) << "temporal cleanup failed";
217  ASSERT_EQ(0U, irt.Capacity()) << "temporal del not empty";
218  CheckDump(&irt, 0, 0);
219
220  // null isn't a valid iref.
221  ASSERT_TRUE(irt.Get(nullptr) == nullptr);
222
223  // Stale lookup.
224  iref0 = irt.Add(cookie, obj0.Get());
225  EXPECT_TRUE(iref0 != nullptr);
226  CheckDump(&irt, 1, 1);
227  ASSERT_TRUE(irt.Remove(cookie, iref0));
228  EXPECT_TRUE(irt.Get(iref0) == nullptr) << "stale lookup succeeded";
229  CheckDump(&irt, 0, 0);
230
231  // Test table resizing.
232  // These ones fit...
233  IndirectRef manyRefs[kTableInitial];
234  for (size_t i = 0; i < kTableInitial; i++) {
235    manyRefs[i] = irt.Add(cookie, obj0.Get());
236    ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
237    CheckDump(&irt, i + 1, 1);
238  }
239  // ...this one causes overflow.
240  iref0 = irt.Add(cookie, obj0.Get());
241  ASSERT_TRUE(iref0 != nullptr);
242  ASSERT_EQ(kTableInitial + 1, irt.Capacity());
243  CheckDump(&irt, kTableInitial + 1, 1);
244
245  for (size_t i = 0; i < kTableInitial; i++) {
246    ASSERT_TRUE(irt.Remove(cookie, manyRefs[i])) << "failed removing " << i;
247    CheckDump(&irt, kTableInitial - i, 1);
248  }
249  // Because of removal order, should have 11 entries, 10 of them holes.
250  ASSERT_EQ(kTableInitial + 1, irt.Capacity());
251
252  ASSERT_TRUE(irt.Remove(cookie, iref0)) << "multi-remove final failed";
253
254  ASSERT_EQ(0U, irt.Capacity()) << "multi-del not empty";
255  CheckDump(&irt, 0, 0);
256}
257
258}  // namespace art
259