Home | History | Annotate | Download | only in tests
      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 
     17 #include <gtest/gtest.h>
     18 
     19 #include <utils/StrongPointer.h>
     20 #include <utils/RefBase.h>
     21 
     22 #include <thread>
     23 #include <atomic>
     24 #include <sched.h>
     25 #include <errno.h>
     26 
     27 // Enhanced version of StrongPointer_test, but using RefBase underneath.
     28 
     29 using namespace android;
     30 
     31 static constexpr int NITERS = 1000000;
     32 
     33 static constexpr int INITIAL_STRONG_VALUE = 1 << 28;  // Mirroring RefBase definition.
     34 
     35 class Foo : public RefBase {
     36 public:
     37     Foo(bool* deleted_check) : mDeleted(deleted_check) {
     38         *mDeleted = false;
     39     }
     40 
     41     ~Foo() {
     42         *mDeleted = true;
     43     }
     44 private:
     45     bool* mDeleted;
     46 };
     47 
     48 TEST(RefBase, StrongMoves) {
     49     bool isDeleted;
     50     Foo* foo = new Foo(&isDeleted);
     51     ASSERT_EQ(INITIAL_STRONG_VALUE, foo->getStrongCount());
     52     ASSERT_FALSE(isDeleted) << "Already deleted...?";
     53     sp<Foo> sp1(foo);
     54     wp<Foo> wp1(sp1);
     55     ASSERT_EQ(1, foo->getStrongCount());
     56     // Weak count includes both strong and weak references.
     57     ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
     58     {
     59         sp<Foo> sp2 = std::move(sp1);
     60         ASSERT_EQ(1, foo->getStrongCount())
     61                 << "std::move failed, incremented refcnt";
     62         ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
     63         // The strong count isn't increasing, let's double check the old object
     64         // is properly reset and doesn't early delete
     65         sp1 = std::move(sp2);
     66     }
     67     ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
     68     {
     69         // Now let's double check it deletes on time
     70         sp<Foo> sp2 = std::move(sp1);
     71     }
     72     ASSERT_TRUE(isDeleted) << "foo was leaked!";
     73     ASSERT_TRUE(wp1.promote().get() == nullptr);
     74 }
     75 
     76 TEST(RefBase, WeakCopies) {
     77     bool isDeleted;
     78     Foo* foo = new Foo(&isDeleted);
     79     EXPECT_EQ(0, foo->getWeakRefs()->getWeakCount());
     80     ASSERT_FALSE(isDeleted) << "Foo (weak) already deleted...?";
     81     wp<Foo> wp1(foo);
     82     EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
     83     {
     84         wp<Foo> wp2 = wp1;
     85         ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());
     86     }
     87     EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());
     88     ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
     89     wp1 = nullptr;
     90     ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur";
     91 }
     92 
     93 
     94 // Set up a situation in which we race with visit2AndRremove() to delete
     95 // 2 strong references.  Bar destructor checks that there are no early
     96 // deletions and prior updates are visible to destructor.
     97 class Bar : public RefBase {
     98 public:
     99     Bar(std::atomic<int>* delete_count) : mVisited1(false), mVisited2(false),
    100             mDeleteCount(delete_count) {
    101     }
    102 
    103     ~Bar() {
    104         EXPECT_TRUE(mVisited1);
    105         EXPECT_TRUE(mVisited2);
    106         (*mDeleteCount)++;
    107     }
    108     bool mVisited1;
    109     bool mVisited2;
    110 private:
    111     std::atomic<int>* mDeleteCount;
    112 };
    113 
    114 static sp<Bar> buffer;
    115 static std::atomic<bool> bufferFull(false);
    116 
    117 // Wait until bufferFull has value val.
    118 static inline void waitFor(bool val) {
    119     while (bufferFull != val) {}
    120 }
    121 
    122 cpu_set_t otherCpus;
    123 
    124 // Divide the cpus we're allowed to run on into myCpus and otherCpus.
    125 // Set origCpus to the processors we were originally allowed to run on.
    126 // Return false if origCpus doesn't include at least processors 0 and 1.
    127 static bool setExclusiveCpus(cpu_set_t* origCpus /* out */,
    128         cpu_set_t* myCpus /* out */, cpu_set_t* otherCpus) {
    129     if (sched_getaffinity(0, sizeof(cpu_set_t), origCpus) != 0) {
    130         return false;
    131     }
    132     if (!CPU_ISSET(0,  origCpus) || !CPU_ISSET(1, origCpus)) {
    133         return false;
    134     }
    135     CPU_ZERO(myCpus);
    136     CPU_ZERO(otherCpus);
    137     CPU_OR(myCpus, myCpus, origCpus);
    138     CPU_OR(otherCpus, otherCpus, origCpus);
    139     for (unsigned i = 0; i < CPU_SETSIZE; ++i) {
    140         // I get the even cores, the other thread gets the odd ones.
    141         if (i & 1) {
    142             CPU_CLR(i, myCpus);
    143         } else {
    144             CPU_CLR(i, otherCpus);
    145         }
    146     }
    147     return true;
    148 }
    149 
    150 static void visit2AndRemove() {
    151     if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
    152         FAIL() << "setaffinity returned:" << errno;
    153     }
    154     for (int i = 0; i < NITERS; ++i) {
    155         waitFor(true);
    156         buffer->mVisited2 = true;
    157         buffer = nullptr;
    158         bufferFull = false;
    159     }
    160 }
    161 
    162 TEST(RefBase, RacingDestructors) {
    163     cpu_set_t origCpus;
    164     cpu_set_t myCpus;
    165     // Restrict us and the helper thread to disjoint cpu sets.
    166     // This prevents us from getting scheduled against each other,
    167     // which would be atrociously slow.
    168     if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
    169         std::thread t(visit2AndRemove);
    170         std::atomic<int> deleteCount(0);
    171         if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
    172             FAIL() << "setaffinity returned:" << errno;
    173         }
    174         for (int i = 0; i < NITERS; ++i) {
    175             waitFor(false);
    176             Bar* bar = new Bar(&deleteCount);
    177             sp<Bar> sp3(bar);
    178             buffer = sp3;
    179             bufferFull = true;
    180             ASSERT_TRUE(bar->getStrongCount() >= 1);
    181             // Weak count includes strong count.
    182             ASSERT_TRUE(bar->getWeakRefs()->getWeakCount() >= 1);
    183             sp3->mVisited1 = true;
    184             sp3 = nullptr;
    185         }
    186         t.join();
    187         if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
    188             FAIL();
    189         }
    190         ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
    191     }  // Otherwise this is slow and probably pointless on a uniprocessor.
    192 }
    193 
    194 static wp<Bar> wpBuffer;
    195 static std::atomic<bool> wpBufferFull(false);
    196 
    197 // Wait until wpBufferFull has value val.
    198 static inline void wpWaitFor(bool val) {
    199     while (wpBufferFull != val) {}
    200 }
    201 
    202 static void visit3AndRemove() {
    203     if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {
    204         FAIL() << "setaffinity returned:" << errno;
    205     }
    206     for (int i = 0; i < NITERS; ++i) {
    207         wpWaitFor(true);
    208         {
    209             sp<Bar> sp1 = wpBuffer.promote();
    210             // We implicitly check that sp1 != NULL
    211             sp1->mVisited2 = true;
    212         }
    213         wpBuffer = nullptr;
    214         wpBufferFull = false;
    215     }
    216 }
    217 
    218 TEST(RefBase, RacingPromotions) {
    219     cpu_set_t origCpus;
    220     cpu_set_t myCpus;
    221     // Restrict us and the helper thread to disjoint cpu sets.
    222     // This prevents us from getting scheduled against each other,
    223     // which would be atrociously slow.
    224     if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {
    225         std::thread t(visit3AndRemove);
    226         std::atomic<int> deleteCount(0);
    227         if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {
    228             FAIL() << "setaffinity returned:" << errno;
    229         }
    230         for (int i = 0; i < NITERS; ++i) {
    231             Bar* bar = new Bar(&deleteCount);
    232             wp<Bar> wp1(bar);
    233             bar->mVisited1 = true;
    234             if (i % (NITERS / 10) == 0) {
    235                 // Do this rarely, since it generates a log message.
    236                 wp1 = nullptr;  // No longer destroys the object.
    237                 wp1 = bar;
    238             }
    239             wpBuffer = wp1;
    240             ASSERT_EQ(bar->getWeakRefs()->getWeakCount(), 2);
    241             wpBufferFull = true;
    242             // Promotion races with that in visit3AndRemove.
    243             // This may or may not succeed, but it shouldn't interfere with
    244             // the concurrent one.
    245             sp<Bar> sp1 = wp1.promote();
    246             wpWaitFor(false);  // Waits for other thread to drop strong pointer.
    247             sp1 = nullptr;
    248             // No strong pointers here.
    249             sp1 = wp1.promote();
    250             ASSERT_EQ(sp1.get(), nullptr) << "Dead wp promotion succeeded!";
    251         }
    252         t.join();
    253         if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {
    254             FAIL();
    255         }
    256         ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!";
    257     }  // Otherwise this is slow and probably pointless on a uniprocessor.
    258 }
    259