diff options
author | Hans Boehm <hboehm@google.com> | 2019-03-04 15:22:10 -0800 |
---|---|---|
committer | Hans Boehm <hboehm@google.com> | 2019-03-09 21:41:37 -0800 |
commit | 029b12ebde0b5a13b4b1252dc20b2ecc8194d5b7 (patch) | |
tree | 09e950cd16c2056d2ca4d36aee57de3374eaf6b1 /libutils/RefBase_test.cpp | |
parent | 949a561cfaf96840101901c6c9448dd3e859a4bd (diff) |
Fix wp and sp comparison bugs
Make clear() actually clear wp m_refs, so that nulls compare equal.
Make equality consistent with < and >, ensuring that a weak pointer
cannot be both equal to and greater than another.
Don't rely on the built-in < and > operators to correctly order
different objects. The standard does not guarantee that, and there is
a risk of compiler relying on that lack of guarantee.
Remove unnecessary comparison overloads, especially those
comparing a wp<> to an sp<>.
Change the remaining wp<> to sp<> comparisons to check for equivalence
of the mRefs pointer instead of the object address, thus eliminating
the dubious equal comparison result for a dead wp<> and an sp<> that
happen to point to the same object address.
Add comparison tests.
Test: Treehugger, boot AOSP, atest RefBase
Bug: 126922090
Change-Id: I15911150e0fc85ace2c4b77d337826e12793c690
Diffstat (limited to 'libutils/RefBase_test.cpp')
-rw-r--r-- | libutils/RefBase_test.cpp | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp index 2e0cf6e46..b594ca56d 100644 --- a/libutils/RefBase_test.cpp +++ b/libutils/RefBase_test.cpp @@ -45,6 +45,44 @@ 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); @@ -90,6 +128,91 @@ TEST(RefBase, WeakCopies) { ASSERT_FALSE(isDeleted) << "Deletion on wp destruction should no longer occur"; } +TEST(RefBase, Comparisons) { + bool isDeleted, isDeleted2; + 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); +} + +// 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); +} // Set up a situation in which we race with visit2AndRremove() to delete // 2 strong references. Bar destructor checks that there are no early |