|  | /* | 
|  | * Copyright (C) 2016 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include <utils/StrongPointer.h> | 
|  | #include <utils/RefBase.h> | 
|  |  | 
|  | #include <thread> | 
|  | #include <atomic> | 
|  | #include <sched.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | // Enhanced version of StrongPointer_test, but using RefBase underneath. | 
|  |  | 
|  | using namespace android; | 
|  |  | 
|  | static constexpr int NITERS = 1000000; | 
|  |  | 
|  | static constexpr int INITIAL_STRONG_VALUE = 1 << 28;  // Mirroring RefBase definition. | 
|  |  | 
|  | class Foo : public RefBase { | 
|  | public: | 
|  | Foo(bool* deleted_check) : mDeleted(deleted_check) { | 
|  | *mDeleted = false; | 
|  | } | 
|  |  | 
|  | ~Foo() { | 
|  | *mDeleted = true; | 
|  | } | 
|  | private: | 
|  | bool* mDeleted; | 
|  | }; | 
|  |  | 
|  | // A version of Foo that ensures that all objects are allocated at the same | 
|  | // address. No more than one can be allocated at a time. Thread-hostile. | 
|  | class FooFixedAlloc : public RefBase { | 
|  | public: | 
|  | static void* operator new(size_t size) { | 
|  | if (mAllocCount != 0) { | 
|  | abort(); | 
|  | } | 
|  | mAllocCount = 1; | 
|  | if (theMemory == nullptr) { | 
|  | theMemory = malloc(size); | 
|  | } | 
|  | return theMemory; | 
|  | } | 
|  |  | 
|  | static void operator delete(void *p) { | 
|  | if (mAllocCount != 1 || p != theMemory) { | 
|  | abort(); | 
|  | } | 
|  | mAllocCount = 0; | 
|  | } | 
|  |  | 
|  | FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) { | 
|  | *mDeleted = false; | 
|  | } | 
|  |  | 
|  | ~FooFixedAlloc() { | 
|  | *mDeleted = true; | 
|  | } | 
|  | private: | 
|  | bool* mDeleted; | 
|  | static int mAllocCount; | 
|  | static void* theMemory; | 
|  | }; | 
|  |  | 
|  | int FooFixedAlloc::mAllocCount(0); | 
|  | void* FooFixedAlloc::theMemory(nullptr); | 
|  |  | 
|  | TEST(RefBase, StrongMoves) { | 
|  | bool isDeleted; | 
|  | Foo* foo = new Foo(&isDeleted); | 
|  | ASSERT_EQ(INITIAL_STRONG_VALUE, foo->getStrongCount()); | 
|  | ASSERT_FALSE(isDeleted) << "Already deleted...?"; | 
|  | sp<Foo> sp1(foo); | 
|  | wp<Foo> wp1(sp1); | 
|  | ASSERT_EQ(1, foo->getStrongCount()); | 
|  | // Weak count includes both strong and weak references. | 
|  | ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount()); | 
|  | { | 
|  | sp<Foo> sp2 = std::move(sp1); | 
|  | ASSERT_EQ(1, foo->getStrongCount()) | 
|  | << "std::move failed, incremented refcnt"; | 
|  | ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid"; | 
|  | // The strong count isn't increasing, let's double check the old object | 
|  | // is properly reset and doesn't early delete | 
|  | sp1 = std::move(sp2); | 
|  | } | 
|  | ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!"; | 
|  | { | 
|  | // Now let's double check it deletes on time | 
|  | sp<Foo> sp2 = std::move(sp1); | 
|  | } | 
|  | ASSERT_TRUE(isDeleted) << "foo was leaked!"; | 
|  | ASSERT_TRUE(wp1.promote().get() == nullptr); | 
|  | } | 
|  |  | 
|  | TEST(RefBase, WeakCopies) { | 
|  | bool isDeleted; | 
|  | Foo* foo = new Foo(&isDeleted); | 
|  | EXPECT_EQ(0, foo->getWeakRefs()->getWeakCount()); | 
|  | ASSERT_FALSE(isDeleted) << "Foo (weak) already deleted...?"; | 
|  | wp<Foo> wp1(foo); | 
|  | EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount()); | 
|  | { | 
|  | wp<Foo> wp2 = wp1; | 
|  | ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount()); | 
|  | } | 
|  | EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount()); | 
|  | ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!"; | 
|  | wp1 = nullptr; | 
|  | ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur"; | 
|  | } | 
|  |  | 
|  | TEST(RefBase, Comparisons) { | 
|  | bool isDeleted, isDeleted2, isDeleted3; | 
|  | Foo* foo = new Foo(&isDeleted); | 
|  | Foo* foo2 = new Foo(&isDeleted2); | 
|  | sp<Foo> sp1(foo); | 
|  | sp<Foo> sp2(foo2); | 
|  | wp<Foo> wp1(sp1); | 
|  | wp<Foo> wp2(sp1); | 
|  | wp<Foo> wp3(sp2); | 
|  | ASSERT_TRUE(wp1 == wp2); | 
|  | ASSERT_TRUE(wp1 == sp1); | 
|  | ASSERT_TRUE(wp3 == sp2); | 
|  | ASSERT_TRUE(wp1 != sp2); | 
|  | ASSERT_TRUE(wp1 <= wp2); | 
|  | ASSERT_TRUE(wp1 >= wp2); | 
|  | ASSERT_FALSE(wp1 != wp2); | 
|  | ASSERT_FALSE(wp1 > wp2); | 
|  | ASSERT_FALSE(wp1 < wp2); | 
|  | ASSERT_FALSE(sp1 == sp2); | 
|  | ASSERT_TRUE(sp1 != sp2); | 
|  | bool sp1_smaller = sp1 < sp2; | 
|  | wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3; | 
|  | wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1; | 
|  | ASSERT_TRUE(wp_smaller < wp_larger); | 
|  | ASSERT_TRUE(wp_smaller != wp_larger); | 
|  | ASSERT_TRUE(wp_smaller <= wp_larger); | 
|  | ASSERT_FALSE(wp_smaller == wp_larger); | 
|  | ASSERT_FALSE(wp_smaller > wp_larger); | 
|  | ASSERT_FALSE(wp_smaller >= wp_larger); | 
|  | sp2 = nullptr; | 
|  | ASSERT_TRUE(isDeleted2); | 
|  | ASSERT_FALSE(isDeleted); | 
|  | ASSERT_FALSE(wp3 == sp2); | 
|  | // Comparison results on weak pointers should not be affected. | 
|  | ASSERT_TRUE(wp_smaller < wp_larger); | 
|  | ASSERT_TRUE(wp_smaller != wp_larger); | 
|  | ASSERT_TRUE(wp_smaller <= wp_larger); | 
|  | ASSERT_FALSE(wp_smaller == wp_larger); | 
|  | ASSERT_FALSE(wp_smaller > wp_larger); | 
|  | ASSERT_FALSE(wp_smaller >= wp_larger); | 
|  | wp2 = nullptr; | 
|  | ASSERT_FALSE(wp1 == wp2); | 
|  | ASSERT_TRUE(wp1 != wp2); | 
|  | wp1.clear(); | 
|  | ASSERT_TRUE(wp1 == wp2); | 
|  | ASSERT_FALSE(wp1 != wp2); | 
|  | wp3.clear(); | 
|  | ASSERT_TRUE(wp1 == wp3); | 
|  | ASSERT_FALSE(wp1 != wp3); | 
|  | ASSERT_FALSE(isDeleted); | 
|  | sp1.clear(); | 
|  | ASSERT_TRUE(isDeleted); | 
|  | ASSERT_TRUE(sp1 == sp2); | 
|  | // Try to check that null pointers are properly initialized. | 
|  | { | 
|  | // Try once with non-null, to maximize chances of getting junk on the | 
|  | // stack. | 
|  | sp<Foo> sp3(new Foo(&isDeleted3)); | 
|  | wp<Foo> wp4(sp3); | 
|  | wp<Foo> wp5; | 
|  | ASSERT_FALSE(wp4 == wp5); | 
|  | ASSERT_TRUE(wp4 != wp5); | 
|  | ASSERT_FALSE(sp3 == wp5); | 
|  | ASSERT_FALSE(wp5 == sp3); | 
|  | ASSERT_TRUE(sp3 != wp5); | 
|  | ASSERT_TRUE(wp5 != sp3); | 
|  | ASSERT_TRUE(sp3 == wp4); | 
|  | } | 
|  | { | 
|  | sp<Foo> sp3; | 
|  | wp<Foo> wp4(sp3); | 
|  | wp<Foo> wp5; | 
|  | ASSERT_TRUE(wp4 == wp5); | 
|  | ASSERT_FALSE(wp4 != wp5); | 
|  | ASSERT_TRUE(sp3 == wp5); | 
|  | ASSERT_TRUE(wp5 == sp3); | 
|  | ASSERT_FALSE(sp3 != wp5); | 
|  | ASSERT_FALSE(wp5 != sp3); | 
|  | ASSERT_TRUE(sp3 == wp4); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check whether comparison against dead wp works, even if the object referenced | 
|  | // by the new wp happens to be at the same address. | 
|  | TEST(RefBase, ReplacedComparison) { | 
|  | bool isDeleted, isDeleted2; | 
|  | FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted); | 
|  | sp<FooFixedAlloc> sp1(foo); | 
|  | wp<FooFixedAlloc> wp1(sp1); | 
|  | ASSERT_TRUE(wp1 == sp1); | 
|  | sp1.clear();  // Deallocates the object. | 
|  | ASSERT_TRUE(isDeleted); | 
|  | FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2); | 
|  | ASSERT_FALSE(isDeleted2); | 
|  | ASSERT_EQ(foo, foo2);  // Not technically a legal comparison, but ... | 
|  | sp<FooFixedAlloc> sp2(foo2); | 
|  | wp<FooFixedAlloc> wp2(sp2); | 
|  | ASSERT_TRUE(sp2 == wp2); | 
|  | ASSERT_FALSE(sp2 != wp2); | 
|  | ASSERT_TRUE(sp2 != wp1); | 
|  | ASSERT_FALSE(sp2 == wp1); | 
|  | ASSERT_FALSE(sp2 == sp1);  // sp1 is null. | 
|  | ASSERT_FALSE(wp1 == wp2);  // wp1 refers to old object. | 
|  | ASSERT_TRUE(wp1 != wp2); | 
|  | ASSERT_TRUE(wp1 > wp2 || wp1 < wp2); | 
|  | ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2); | 
|  | ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2); | 
|  | ASSERT_FALSE(wp1 == nullptr); | 
|  | wp1 = sp2; | 
|  | ASSERT_TRUE(wp1 == wp2); | 
|  | ASSERT_FALSE(wp1 != wp2); | 
|  | } | 
|  |  | 
|  | TEST(RefBase, AssertWeakRefExistsSuccess) { | 
|  | bool isDeleted; | 
|  | sp<Foo> foo = sp<Foo>::make(&isDeleted); | 
|  | wp<Foo> weakFoo = foo; | 
|  |  | 
|  | EXPECT_EQ(weakFoo, wp<Foo>::fromExisting(foo.get())); | 
|  | EXPECT_EQ(weakFoo.unsafe_get(), wp<Foo>::fromExisting(foo.get()).unsafe_get()); | 
|  |  | 
|  | EXPECT_FALSE(isDeleted); | 
|  | foo = nullptr; | 
|  | EXPECT_TRUE(isDeleted); | 
|  | } | 
|  |  | 
|  | TEST(RefBase, AssertWeakRefExistsDeath) { | 
|  | // uses some other refcounting method, or none at all | 
|  | bool isDeleted; | 
|  | Foo* foo = new Foo(&isDeleted); | 
|  |  | 
|  | // can only get a valid wp<> object when you construct it from an sp<> | 
|  | EXPECT_DEATH(wp<Foo>::fromExisting(foo), ""); | 
|  |  | 
|  | delete foo; | 
|  | } | 
|  |  | 
|  | TEST(RefBase, DoubleOwnershipDeath) { | 
|  | bool isDeleted; | 
|  | auto foo = sp<Foo>::make(&isDeleted); | 
|  |  | 
|  | // if something else thinks it owns foo, should die | 
|  | EXPECT_DEATH(delete foo.get(), ""); | 
|  |  | 
|  | EXPECT_FALSE(isDeleted); | 
|  | } | 
|  |  | 
|  | TEST(RefBase, StackOwnershipDeath) { | 
|  | bool isDeleted; | 
|  | EXPECT_DEATH({ Foo foo(&isDeleted); foo.incStrong(nullptr); }, ""); | 
|  | } | 
|  |  | 
|  | // Set up a situation in which we race with visit2AndRremove() to delete | 
|  | // 2 strong references.  Bar destructor checks that there are no early | 
|  | // deletions and prior updates are visible to destructor. | 
|  | class Bar : public RefBase { | 
|  | public: | 
|  | Bar(std::atomic<int>* delete_count) : mVisited1(false), mVisited2(false), | 
|  | mDeleteCount(delete_count) { | 
|  | } | 
|  |  | 
|  | ~Bar() { | 
|  | EXPECT_TRUE(mVisited1); | 
|  | EXPECT_TRUE(mVisited2); | 
|  | (*mDeleteCount)++; | 
|  | } | 
|  | bool mVisited1; | 
|  | bool mVisited2; | 
|  | private: | 
|  | std::atomic<int>* mDeleteCount; | 
|  | }; | 
|  |  | 
|  | static sp<Bar> buffer; | 
|  | static std::atomic<bool> bufferFull(false); | 
|  |  | 
|  | // Wait until bufferFull has value val. | 
|  | static inline void waitFor(bool val) { | 
|  | while (bufferFull != val) {} | 
|  | } | 
|  |  | 
|  | cpu_set_t otherCpus; | 
|  |  | 
|  | // Divide the cpus we're allowed to run on into myCpus and otherCpus. | 
|  | // Set origCpus to the processors we were originally allowed to run on. | 
|  | // Return false if origCpus doesn't include at least processors 0 and 1. | 
|  | static bool setExclusiveCpus(cpu_set_t* origCpus /* out */, | 
|  | cpu_set_t* myCpus /* out */, cpu_set_t* otherCpus) { | 
|  | if (sched_getaffinity(0, sizeof(cpu_set_t), origCpus) != 0) { | 
|  | return false; | 
|  | } | 
|  | if (!CPU_ISSET(0,  origCpus) || !CPU_ISSET(1, origCpus)) { | 
|  | return false; | 
|  | } | 
|  | CPU_ZERO(myCpus); | 
|  | CPU_ZERO(otherCpus); | 
|  | CPU_OR(myCpus, myCpus, origCpus); | 
|  | CPU_OR(otherCpus, otherCpus, origCpus); | 
|  | for (unsigned i = 0; i < CPU_SETSIZE; ++i) { | 
|  | // I get the even cores, the other thread gets the odd ones. | 
|  | if (i & 1) { | 
|  | CPU_CLR(i, myCpus); | 
|  | } else { | 
|  | CPU_CLR(i, otherCpus); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void visit2AndRemove() { | 
|  | if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) { | 
|  | FAIL() << "setaffinity returned:" << errno; | 
|  | } | 
|  | for (int i = 0; i < NITERS; ++i) { | 
|  | waitFor(true); | 
|  | buffer->mVisited2 = true; | 
|  | buffer = nullptr; | 
|  | bufferFull = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(RefBase, RacingDestructors) { | 
|  | cpu_set_t origCpus; | 
|  | cpu_set_t myCpus; | 
|  | // Restrict us and the helper thread to disjoint cpu sets. | 
|  | // This prevents us from getting scheduled against each other, | 
|  | // which would be atrociously slow. | 
|  | if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) { | 
|  | std::thread t(visit2AndRemove); | 
|  | std::atomic<int> deleteCount(0); | 
|  | if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) { | 
|  | FAIL() << "setaffinity returned:" << errno; | 
|  | } | 
|  | for (int i = 0; i < NITERS; ++i) { | 
|  | waitFor(false); | 
|  | Bar* bar = new Bar(&deleteCount); | 
|  | sp<Bar> sp3(bar); | 
|  | buffer = sp3; | 
|  | bufferFull = true; | 
|  | ASSERT_TRUE(bar->getStrongCount() >= 1); | 
|  | // Weak count includes strong count. | 
|  | ASSERT_TRUE(bar->getWeakRefs()->getWeakCount() >= 1); | 
|  | sp3->mVisited1 = true; | 
|  | sp3 = nullptr; | 
|  | } | 
|  | t.join(); | 
|  | if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) { | 
|  | FAIL(); | 
|  | } | 
|  | ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!"; | 
|  | }  // Otherwise this is slow and probably pointless on a uniprocessor. | 
|  | } | 
|  |  | 
|  | static wp<Bar> wpBuffer; | 
|  | static std::atomic<bool> wpBufferFull(false); | 
|  |  | 
|  | // Wait until wpBufferFull has value val. | 
|  | static inline void wpWaitFor(bool val) { | 
|  | while (wpBufferFull != val) {} | 
|  | } | 
|  |  | 
|  | static void visit3AndRemove() { | 
|  | if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) { | 
|  | FAIL() << "setaffinity returned:" << errno; | 
|  | } | 
|  | for (int i = 0; i < NITERS; ++i) { | 
|  | wpWaitFor(true); | 
|  | { | 
|  | sp<Bar> sp1 = wpBuffer.promote(); | 
|  | // We implicitly check that sp1 != NULL | 
|  | sp1->mVisited2 = true; | 
|  | } | 
|  | wpBuffer = nullptr; | 
|  | wpBufferFull = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(RefBase, RacingPromotions) { | 
|  | cpu_set_t origCpus; | 
|  | cpu_set_t myCpus; | 
|  | // Restrict us and the helper thread to disjoint cpu sets. | 
|  | // This prevents us from getting scheduled against each other, | 
|  | // which would be atrociously slow. | 
|  | if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) { | 
|  | std::thread t(visit3AndRemove); | 
|  | std::atomic<int> deleteCount(0); | 
|  | if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) { | 
|  | FAIL() << "setaffinity returned:" << errno; | 
|  | } | 
|  | for (int i = 0; i < NITERS; ++i) { | 
|  | Bar* bar = new Bar(&deleteCount); | 
|  | wp<Bar> wp1(bar); | 
|  | bar->mVisited1 = true; | 
|  | if (i % (NITERS / 10) == 0) { | 
|  | // Do this rarely, since it generates a log message. | 
|  | wp1 = nullptr;  // No longer destroys the object. | 
|  | wp1 = bar; | 
|  | } | 
|  | wpBuffer = wp1; | 
|  | ASSERT_EQ(bar->getWeakRefs()->getWeakCount(), 2); | 
|  | wpBufferFull = true; | 
|  | // Promotion races with that in visit3AndRemove. | 
|  | // This may or may not succeed, but it shouldn't interfere with | 
|  | // the concurrent one. | 
|  | sp<Bar> sp1 = wp1.promote(); | 
|  | wpWaitFor(false);  // Waits for other thread to drop strong pointer. | 
|  | sp1 = nullptr; | 
|  | // No strong pointers here. | 
|  | sp1 = wp1.promote(); | 
|  | ASSERT_EQ(sp1.get(), nullptr) << "Dead wp promotion succeeded!"; | 
|  | } | 
|  | t.join(); | 
|  | if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) { | 
|  | FAIL(); | 
|  | } | 
|  | ASSERT_EQ(NITERS, deleteCount) << "Deletions missed!"; | 
|  | }  // Otherwise this is slow and probably pointless on a uniprocessor. | 
|  | } |