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