/* * * Copyright 2017 gRPC authors. * * 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 "src/core/lib/gprpp/ref_counted.h" #include #include #include #include "src/core/lib/gprpp/memory.h" #include "test/core/util/test_config.h" namespace grpc_core { namespace testing { namespace { class Foo : public RefCounted { public: Foo() { static_assert(std::has_virtual_destructor::value, "PolymorphicRefCount doesn't have a virtual dtor"); } }; TEST(RefCounted, Basic) { Foo* foo = new Foo(); foo->Unref(); } TEST(RefCounted, ExtraRef) { Foo* foo = new Foo(); RefCountedPtr foop = foo->Ref(); foop.release(); foo->Unref(); foo->Unref(); } class Value : public RefCounted { public: Value(int value, std::set>* registry) : value_(value) { registry->emplace(this); } int value() const { return value_; } private: int value_; }; void GarbageCollectRegistry(std::set>* registry) { for (auto it = registry->begin(); it != registry->end();) { RefCountedPtr v = (*it)->RefIfNonZero(); // Check if the object has any refs remaining. if (v != nullptr) { // It has refs remaining, so we do not delete it. ++it; } else { // No refs remaining, so remove it from the registry. it = registry->erase(it); } } } TEST(RefCounted, NoDeleteUponUnref) { std::set> registry; // Add two objects to the registry. auto v1 = MakeRefCounted(1, ®istry); auto v2 = MakeRefCounted(2, ®istry); EXPECT_THAT(registry, ::testing::UnorderedElementsAre( ::testing::Pointee(::testing::Property(&Value::value, 1)), ::testing::Pointee(::testing::Property(&Value::value, 2)))); // Running garbage collection should not delete anything, since both // entries still have refs. GarbageCollectRegistry(®istry); EXPECT_THAT(registry, ::testing::UnorderedElementsAre( ::testing::Pointee(::testing::Property(&Value::value, 1)), ::testing::Pointee(::testing::Property(&Value::value, 2)))); // Unref v2 and run GC to remove it. v2.reset(); GarbageCollectRegistry(®istry); EXPECT_THAT(registry, ::testing::UnorderedElementsAre(::testing::Pointee( ::testing::Property(&Value::value, 1)))); // Now unref v1 and run GC again. v1.reset(); GarbageCollectRegistry(®istry); EXPECT_THAT(registry, ::testing::UnorderedElementsAre()); } class ValueInExternalAllocation : public RefCounted { public: explicit ValueInExternalAllocation(int value) : value_(value) {} int value() const { return value_; } private: int value_; }; TEST(RefCounted, CallDtorUponUnref) { std::aligned_storage::type storage; RefCountedPtr value( new (&storage) ValueInExternalAllocation(5)); EXPECT_EQ(value->value(), 5); } class FooNonPolymorphic : public RefCounted { public: FooNonPolymorphic() { static_assert(!std::has_virtual_destructor::value, "NonPolymorphicRefCount has a virtual dtor"); } }; TEST(RefCountedNonPolymorphic, Basic) { FooNonPolymorphic* foo = new FooNonPolymorphic(); foo->Unref(); } TEST(RefCountedNonPolymorphic, ExtraRef) { FooNonPolymorphic* foo = new FooNonPolymorphic(); RefCountedPtr foop = foo->Ref(); foop.release(); foo->Unref(); foo->Unref(); } class FooWithTracing : public RefCounted { public: FooWithTracing() : RefCounted("Foo") {} }; TEST(RefCountedWithTracing, Basic) { FooWithTracing* foo = new FooWithTracing(); RefCountedPtr foop = foo->Ref(DEBUG_LOCATION, "extra_ref"); foop.release(); foo->Unref(DEBUG_LOCATION, "extra_ref"); // Can use the no-argument methods, too. foop = foo->Ref(); foop.release(); foo->Unref(); foo->Unref(DEBUG_LOCATION, "original_ref"); } class FooNonPolymorphicWithTracing : public RefCounted { public: FooNonPolymorphicWithTracing() : RefCounted("FooNonPolymorphicWithTracing") {} }; TEST(RefCountedNonPolymorphicWithTracing, Basic) { FooNonPolymorphicWithTracing* foo = new FooNonPolymorphicWithTracing(); RefCountedPtr foop = foo->Ref(DEBUG_LOCATION, "extra_ref"); foop.release(); foo->Unref(DEBUG_LOCATION, "extra_ref"); // Can use the no-argument methods, too. foop = foo->Ref(); foop.release(); foo->Unref(); foo->Unref(DEBUG_LOCATION, "original_ref"); } } // namespace } // namespace testing } // namespace grpc_core int main(int argc, char** argv) { grpc::testing::TestEnvironment env(argc, argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }