| // Copyright 2016 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This file tests both ref_counted.h and ref_ptr.h (which the former includes). |
| // TODO(vtl): Possibly we could separate these tests out better, since a lot of |
| // it is actually testing |RefPtr|. |
| |
| #include "src/lib/fxl/memory/ref_counted.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/fxl/macros.h" |
| |
| #if defined(__clang__) |
| #define ALLOW_PESSIMIZING_MOVE(code_line) \ |
| _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpessimizing-move\"") \ |
| code_line; \ |
| _Pragma("clang diagnostic pop") |
| #else |
| #define ALLOW_PESSIMIZING_MOVE(code_line) code_line; |
| #endif |
| |
| #if defined(__clang__) |
| #define ALLOW_SELF_MOVE(code_line) \ |
| _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wself-move\"") code_line; \ |
| _Pragma("clang diagnostic pop") |
| #else |
| #define ALLOW_SELF_MOVE(code_line) code_line; |
| #endif |
| |
| #if defined(__clang__) |
| #define ALLOW_SELF_ASSIGN_OVERLOADED(code_line) \ |
| _Pragma("clang diagnostic push") \ |
| _Pragma("clang diagnostic ignored \"-Wself-assign-overloaded\"") code_line; \ |
| _Pragma("clang diagnostic pop") |
| #else |
| #define ALLOW_SELF_ASSIGN_OVERLOADED(code_line) code_line; |
| #endif |
| |
| namespace fxl { |
| namespace { |
| |
| class MyClass : public RefCountedThreadSafe<MyClass> { |
| protected: |
| MyClass(MyClass** created, bool* was_destroyed) : was_destroyed_(was_destroyed) { |
| if (created) |
| *created = this; |
| } |
| virtual ~MyClass() { |
| if (was_destroyed_) |
| *was_destroyed_ = true; |
| } |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(MyClass); |
| FRIEND_MAKE_REF_COUNTED(MyClass); |
| |
| bool* was_destroyed_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(MyClass); |
| }; |
| |
| class MySubclass final : public MyClass { |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(MySubclass); |
| FRIEND_MAKE_REF_COUNTED(MySubclass); |
| |
| MySubclass(MySubclass** created, bool* was_destroyed) : MyClass(nullptr, was_destroyed) { |
| if (created) |
| *created = this; |
| } |
| ~MySubclass() override {} |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(MySubclass); |
| }; |
| |
| TEST(RefCountedTest, Constructors) { |
| bool was_destroyed; |
| |
| { |
| // Default. |
| RefPtr<MyClass> r; |
| EXPECT_TRUE(r.get() == nullptr); |
| EXPECT_FALSE(r); |
| } |
| |
| { |
| // Nullptr. |
| RefPtr<MyClass> r(nullptr); |
| EXPECT_TRUE(r.get() == nullptr); |
| EXPECT_FALSE(r); |
| } |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| // Adopt, then RVO. |
| RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| EXPECT_TRUE(created); |
| EXPECT_EQ(created, r.get()); |
| EXPECT_TRUE(r); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| // Adopt, then move. |
| ALLOW_PESSIMIZING_MOVE( |
| RefPtr<MyClass> r(std::move(MakeRefCounted<MyClass>(&created, &was_destroyed)))) |
| EXPECT_TRUE(created); |
| EXPECT_EQ(created, r.get()); |
| EXPECT_TRUE(r); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| // Copy. |
| RefPtr<MyClass> r2(r1); |
| EXPECT_TRUE(created); |
| EXPECT_EQ(created, r1.get()); |
| EXPECT_EQ(created, r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| // From raw pointer. |
| RefPtr<MyClass> r2(created); |
| EXPECT_TRUE(created); |
| EXPECT_EQ(created, r1.get()); |
| EXPECT_EQ(created, r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MySubclass* created = nullptr; |
| was_destroyed = false; |
| // Adopt, then "move". |
| RefPtr<MyClass> r(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
| EXPECT_TRUE(created); |
| EXPECT_EQ(static_cast<MyClass*>(created), r.get()); |
| EXPECT_TRUE(r); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MySubclass* created = nullptr; |
| was_destroyed = false; |
| // Adopt, then "move". |
| ALLOW_PESSIMIZING_MOVE( |
| RefPtr<MyClass> r(std::move(MakeRefCounted<MySubclass>(&created, &was_destroyed)))) |
| EXPECT_TRUE(created); |
| EXPECT_EQ(static_cast<MyClass*>(created), r.get()); |
| EXPECT_TRUE(r); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MySubclass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
| // "Copy". |
| RefPtr<MyClass> r2(r1); |
| EXPECT_TRUE(created); |
| EXPECT_EQ(static_cast<MyClass*>(created), r1.get()); |
| EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MySubclass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
| // From raw pointer. |
| RefPtr<MyClass> r2(created); |
| EXPECT_TRUE(created); |
| EXPECT_EQ(static_cast<MyClass*>(created), r1.get()); |
| EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| } |
| |
| TEST(RefCountedTest, NullAssignmentToNull) { |
| RefPtr<MyClass> r1; |
| // No-op null assignment using |nullptr|. |
| r1 = nullptr; |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_FALSE(r1); |
| |
| RefPtr<MyClass> r2; |
| // No-op null assignment using copy constructor. |
| r1 = r2; |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r2.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r2); |
| |
| // No-op null assignment using move constructor. |
| r1 = std::move(r2); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r2.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r2); |
| |
| RefPtr<MySubclass> r3; |
| // No-op null assignment using "copy" constructor. |
| r1 = r3; |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r3.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r3); |
| |
| // No-op null assignment using "move" constructor. |
| r1 = std::move(r3); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r3.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r3); |
| } |
| |
| TEST(RefCountedTest, NonNullAssignmentToNull) { |
| bool was_destroyed; |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| RefPtr<MyClass> r2; |
| // Copy assignment (to null ref pointer). |
| r2 = r1; |
| EXPECT_EQ(created, r1.get()); |
| EXPECT_EQ(created, r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| RefPtr<MyClass> r2; |
| // Move assignment (to null ref pointer). |
| r2 = std::move(r1); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_EQ(created, r2.get()); |
| EXPECT_FALSE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MySubclass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
| RefPtr<MyClass> r2; |
| // "Copy" assignment (to null ref pointer). |
| r2 = r1; |
| EXPECT_EQ(created, r1.get()); |
| EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MySubclass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
| RefPtr<MyClass> r2; |
| // "Move" assignment (to null ref pointer). |
| r2 = std::move(r1); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_EQ(static_cast<MyClass*>(created), r2.get()); |
| EXPECT_FALSE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| } |
| |
| TEST(RefCountedTest, NullAssignmentToNonNull) { |
| bool was_destroyed = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed)); |
| // Null assignment (to non-null ref pointer) using |nullptr|. |
| r1 = nullptr; |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_TRUE(was_destroyed); |
| |
| was_destroyed = false; |
| r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
| RefPtr<MyClass> r2; |
| // Null assignment (to non-null ref pointer) using copy constructor. |
| r1 = r2; |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r2.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r2); |
| EXPECT_TRUE(was_destroyed); |
| |
| was_destroyed = false; |
| r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
| // Null assignment using move constructor. |
| r1 = std::move(r2); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r2.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r2); |
| EXPECT_TRUE(was_destroyed); |
| |
| was_destroyed = false; |
| r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
| RefPtr<MySubclass> r3; |
| // Null assignment (to non-null ref pointer) using "copy" constructor. |
| r1 = r3; |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r3.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r3); |
| EXPECT_TRUE(was_destroyed); |
| |
| was_destroyed = false; |
| r1 = MakeRefCounted<MyClass>(nullptr, &was_destroyed); |
| // Null assignment (to non-null ref pointer) using "move" constructor. |
| r1 = std::move(r3); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_TRUE(r3.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_FALSE(r3); |
| EXPECT_TRUE(was_destroyed); |
| } |
| |
| TEST(RefCountedTest, NonNullAssignmentToNonNull) { |
| bool was_destroyed1; |
| bool was_destroyed2; |
| |
| { |
| was_destroyed1 = false; |
| was_destroyed2 = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed1)); |
| RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
| // Copy assignment (to non-null ref pointer). |
| r2 = r1; |
| EXPECT_EQ(r1.get(), r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed1); |
| EXPECT_TRUE(was_destroyed2); |
| } |
| EXPECT_TRUE(was_destroyed1); |
| |
| { |
| was_destroyed1 = false; |
| was_destroyed2 = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(nullptr, &was_destroyed1)); |
| RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
| // Move assignment (to non-null ref pointer). |
| r2 = std::move(r1); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_FALSE(r2.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed1); |
| EXPECT_TRUE(was_destroyed2); |
| } |
| EXPECT_TRUE(was_destroyed1); |
| |
| { |
| was_destroyed1 = false; |
| was_destroyed2 = false; |
| RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(nullptr, &was_destroyed1)); |
| RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
| // "Copy" assignment (to non-null ref pointer). |
| r2 = r1; |
| EXPECT_EQ(r1.get(), r2.get()); |
| EXPECT_TRUE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed1); |
| EXPECT_TRUE(was_destroyed2); |
| } |
| EXPECT_TRUE(was_destroyed1); |
| |
| { |
| was_destroyed1 = false; |
| was_destroyed2 = false; |
| RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(nullptr, &was_destroyed1)); |
| RefPtr<MyClass> r2(MakeRefCounted<MyClass>(nullptr, &was_destroyed2)); |
| // Move assignment (to non-null ref pointer). |
| r2 = std::move(r1); |
| EXPECT_TRUE(r1.get() == nullptr); |
| EXPECT_FALSE(r2.get() == nullptr); |
| EXPECT_FALSE(r1); |
| EXPECT_TRUE(r2); |
| EXPECT_FALSE(was_destroyed1); |
| EXPECT_TRUE(was_destroyed2); |
| } |
| EXPECT_TRUE(was_destroyed1); |
| } |
| |
| TEST(RefCountedTest, SelfAssignment) { |
| bool was_destroyed; |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| ALLOW_SELF_ASSIGN_OVERLOADED(r = r) |
| EXPECT_EQ(created, r.get()); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| |
| { |
| MyClass* created = nullptr; |
| was_destroyed = false; |
| RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| // Move. |
| ALLOW_SELF_MOVE(r = std::move(r)) |
| EXPECT_EQ(created, r.get()); |
| EXPECT_FALSE(was_destroyed); |
| } |
| EXPECT_TRUE(was_destroyed); |
| } |
| |
| TEST(RefCountedTest, Swap) { |
| bool was_destroyed1, was_destroyed2; |
| { |
| MyClass* created1 = nullptr; |
| was_destroyed1 = false; |
| RefPtr<MyClass> r1(MakeRefCounted<MyClass>(&created1, &was_destroyed1)); |
| EXPECT_TRUE(created1); |
| EXPECT_EQ(created1, r1.get()); |
| |
| MyClass* created2 = nullptr; |
| was_destroyed2 = false; |
| RefPtr<MyClass> r2(MakeRefCounted<MyClass>(&created2, &was_destroyed2)); |
| EXPECT_TRUE(created2); |
| EXPECT_EQ(created2, r2.get()); |
| EXPECT_NE(created1, created2); |
| |
| r1.swap(r2); |
| EXPECT_EQ(created2, r1.get()); |
| EXPECT_EQ(created1, r2.get()); |
| } |
| } |
| |
| TEST(RefCountedTest, GetAndDereferenceOperators) { |
| // Note: We check here that .get(), operator*, and operator-> are const, but |
| // return non-const pointers/refs. |
| |
| MyClass* created = nullptr; |
| const RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, nullptr)); |
| MyClass* ptr = r.get(); // Assign to non-const pointer. |
| EXPECT_EQ(created, ptr); |
| ptr = r.operator->(); // Assign to non-const pointer. |
| EXPECT_EQ(created, ptr); |
| MyClass& ref = *r; // "Assign" to non-const reference. |
| EXPECT_EQ(created, &ref); |
| } |
| |
| // You can manually call |AddRef()| and |Release()| if you want. |
| TEST(RefCountedTest, AddRefRelease) { |
| MyClass* created = nullptr; |
| bool was_destroyed = false; |
| { |
| RefPtr<MyClass> r(MakeRefCounted<MyClass>(&created, &was_destroyed)); |
| EXPECT_EQ(created, r.get()); |
| created->AddRef(); |
| } |
| EXPECT_FALSE(was_destroyed); |
| created->Release(); |
| EXPECT_TRUE(was_destroyed); |
| } |
| |
| TEST(RefCountedTest, Mix) { |
| MySubclass* created = nullptr; |
| bool was_destroyed = false; |
| RefPtr<MySubclass> r1(MakeRefCounted<MySubclass>(&created, &was_destroyed)); |
| ASSERT_FALSE(was_destroyed); |
| EXPECT_TRUE(created->HasOneRef()); |
| created->AssertHasOneRef(); |
| |
| RefPtr<MySubclass> r2 = r1; |
| ASSERT_FALSE(was_destroyed); |
| EXPECT_FALSE(created->HasOneRef()); |
| |
| r1 = nullptr; |
| ASSERT_FALSE(was_destroyed); |
| created->AssertHasOneRef(); |
| |
| { |
| RefPtr<MyClass> r3 = r2; |
| EXPECT_FALSE(created->HasOneRef()); |
| { |
| RefPtr<MyClass> r4(r3); |
| r2 = nullptr; |
| ASSERT_FALSE(was_destroyed); |
| EXPECT_FALSE(created->HasOneRef()); |
| } |
| ASSERT_FALSE(was_destroyed); |
| EXPECT_TRUE(created->HasOneRef()); |
| created->AssertHasOneRef(); |
| |
| r1 = RefPtr<MySubclass>(static_cast<MySubclass*>(r3.get())); |
| ASSERT_FALSE(was_destroyed); |
| EXPECT_FALSE(created->HasOneRef()); |
| } |
| ASSERT_FALSE(was_destroyed); |
| EXPECT_TRUE(created->HasOneRef()); |
| created->AssertHasOneRef(); |
| |
| EXPECT_EQ(created, r1.get()); |
| |
| r1 = nullptr; |
| EXPECT_TRUE(was_destroyed); |
| } |
| |
| class MyPublicClass : public RefCountedThreadSafe<MyPublicClass> { |
| public: |
| // Overloaded constructors work with |MakeRefCounted()|. |
| MyPublicClass() : has_num_(false), num_(0) {} |
| explicit MyPublicClass(int num) : has_num_(true), num_(num) {} |
| |
| ~MyPublicClass() {} |
| |
| bool has_num() const { return has_num_; } |
| int num() const { return num_; } |
| |
| private: |
| bool has_num_; |
| int num_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(MyPublicClass); |
| }; |
| |
| // You can also just keep constructors and destructors public. Make sure that |
| // works (mostly that it compiles). |
| TEST(RefCountedTest, PublicCtorAndDtor) { |
| RefPtr<MyPublicClass> r1 = MakeRefCounted<MyPublicClass>(); |
| ASSERT_TRUE(r1); |
| EXPECT_FALSE(r1->has_num()); |
| |
| RefPtr<MyPublicClass> r2 = MakeRefCounted<MyPublicClass>(123); |
| ASSERT_TRUE(r2); |
| EXPECT_TRUE(r2->has_num()); |
| EXPECT_EQ(123, r2->num()); |
| EXPECT_NE(r1.get(), r2.get()); |
| |
| r1 = r2; |
| EXPECT_TRUE(r1->has_num()); |
| EXPECT_EQ(123, r1->num()); |
| EXPECT_EQ(r1.get(), r2.get()); |
| |
| r2 = nullptr; |
| EXPECT_FALSE(r2); |
| EXPECT_TRUE(r1->has_num()); |
| EXPECT_EQ(123, r1->num()); |
| |
| r1 = nullptr; |
| EXPECT_FALSE(r1); |
| } |
| |
| // This class saves the value from a RefPtr to a given location in its |
| // destructor. It is designed to test the value of a refptr from within the |
| // destructor of the object the RefPtr is destroying. |
| class SaveValueInDestructor : public RefCountedThreadSafe<SaveValueInDestructor> { |
| public: |
| SaveValueInDestructor() = default; |
| ~SaveValueInDestructor() { *output_ptr_ = save_ref_ptr_->get(); } |
| |
| // Call after constructing. |
| void SetToSave(RefPtr<SaveValueInDestructor>* to_save, SaveValueInDestructor** save_here) { |
| save_ref_ptr_ = to_save; |
| output_ptr_ = save_here; |
| } |
| |
| private: |
| RefPtr<SaveValueInDestructor>* save_ref_ptr_; |
| SaveValueInDestructor** output_ptr_ = nullptr; |
| }; |
| |
| TEST(RefCountedTest, RefPtrRelease) { |
| RefPtr<SaveValueInDestructor> r1 = MakeRefCounted<SaveValueInDestructor>(); |
| // Initialize with an arbitrary non-null value so we can tell when it's |
| // assigned to null. |
| SaveValueInDestructor* saved_r1 = reinterpret_cast<SaveValueInDestructor*>(1); |
| r1->SetToSave(&r1, &saved_r1); |
| |
| // Calling reset() should delete the value, and it should see its own pointer |
| // being null in the refptr during the call. |
| r1.reset(); |
| EXPECT_FALSE(saved_r1); |
| } |
| |
| // TODO(vtl): Add (threaded) stress tests. |
| |
| } // namespace |
| } // namespace fxl |