From 3d1be9c94e1a8203e69b4567207da366421da666 Mon Sep 17 00:00:00 2001 From: WeijieSun Date: Wed, 16 May 2018 16:28:02 +0800 Subject: [PATCH] task: fix based on code review --- include/dsn/c/api_task.h | 16 + include/dsn/cpp/clientlet.h | 5 +- include/dsn/tool-api/task.h | 47 +-- include/dsn/utility/autoref_ptr.h | 4 + src/core/tests/autoref_ptr_test.cpp | 574 ++++++++++++++++++++++++++++ 5 files changed, 618 insertions(+), 28 deletions(-) create mode 100644 src/core/tests/autoref_ptr_test.cpp diff --git a/include/dsn/c/api_task.h b/include/dsn/c/api_task.h index f935864811..15646a9fb1 100644 --- a/include/dsn/c/api_task.h +++ b/include/dsn/c/api_task.h @@ -47,8 +47,24 @@ namespace dsn { typedef std::function task_handler; + +/// A callback to handle rpc requests. +/// +/// Parameters: +/// - dsn_message_t: the received rpc request typedef std::function rpc_request_handler; + +/// A callback to handle rpc responses. +/// +/// Parameters: +/// - error_code +/// - dsn_message_t: the sent rpc request +/// - dsn_message_t: the received rpc response typedef std::function rpc_response_handler; + +/// Parameters: +/// - error_code +/// - size_t: the read or written size of bytes from file. typedef std::function aio_handler; class task; diff --git a/include/dsn/cpp/clientlet.h b/include/dsn/cpp/clientlet.h index 41d6001752..52c8ca6706 100644 --- a/include/dsn/cpp/clientlet.h +++ b/include/dsn/cpp/clientlet.h @@ -178,13 +178,14 @@ template typename std::enable_if::value, rpc_response_task_ptr>::type create_rpc_response_task(dsn_message_t req, task_tracker *tracker, - TCallback &&cb, + TCallback &&callback, int reply_thread_hash = 0) { return create_rpc_response_task( req, tracker, - [cb_fwd = std::move(cb)](error_code err, dsn_message_t req, dsn_message_t resp) mutable { + [cb_fwd = + std::move(callback)](error_code err, dsn_message_t req, dsn_message_t resp) mutable { typename is_typed_rpc_callback::response_t response = {}; if (err == ERR_OK) { unmarshall(resp, response); diff --git a/include/dsn/tool-api/task.h b/include/dsn/tool-api/task.h index 81f48417d2..0f35b88093 100644 --- a/include/dsn/tool-api/task.h +++ b/include/dsn/tool-api/task.h @@ -93,21 +93,16 @@ struct __tls_dsn__ extern __thread struct __tls_dsn__ tls_dsn; /// -/// Task is a component to support the asynchronous programming model in rDSN. +/// Task is a thread-like execution piece that is much lighter than a normal thread. +/// Huge number of tasks may be hosted by a small number of actual threads run within +/// a thread pool. /// -/// Generally speaking, it's a mixture of "future" and "promise": when a task is created, -/// "data provider" can use "promise"-like semantics to store value in it; -/// on the other hand, "data consumer" can also use "future"-like semantics to: -/// 1. wait a value to be ready -/// 2. assigining a callback, and execute the callback when the value is ready -/// 3. cancel the execution of callback -/// -/// When creating the task, user must use several parameters to specify in which thread the -/// callback should run. This is why you need 3 parameters to create the task: +/// When creating the task, user must use 3 parameters to specify in which thread the +/// callback should run: /// /// 1. node: specifies the computation engine of the callback, i.e, the "pool" of "thread_pool" /// 2. task_code: a index to the "task_spec". task_spec specifies which thread pool of -/// the computation engine to run the callback. some other task informations are also +/// the computation engine to run the callback. some other task information is also /// recorded in task_spec. please refer to @task_code, @task_spec, @thread_pool_code /// for more details. /// 3. hash: specifies which thread in the thread pool to execute the callback. (This is @@ -116,19 +111,19 @@ extern __thread struct __tls_dsn__ tls_dsn; /// /// So the running thread of callback will be determined hierarchically: /// -/// |<---determined by "node" -/// | |<-----determined by "code" -/// | | |-------------determined by "hash" -/// | | | -/// |------V--------|-------|---------------------------| |--------------| -/// | |-------------V-------|-----| |-------------| | | | -/// | | |--------| |----V---| | | | | | | -/// | | | thread | ... | thread | | | | | | | -/// | | |--------| |--------| | | | | | | -/// | | thread pool | ... | thread pool | | | | -/// | |---------------------------| |-------------| | | | -/// | service node | ... | service node | -/// |---------------------------------------------------| |--------------| +/// |<---determined by "node" +/// | |<-----determined by "code" +/// | | |-------------determined by "hash" +/// | | | +/// |------V--------|-------|---------------------------| |--------------| +/// | |-------------V-------|-----| |-------------| | | | +/// | | |--------| |----V---| | | | | | | +/// | | | thread | ... | thread | | | | | | | +/// | | |--------| |--------| | | | | | | +/// | | thread pool | ... | thread pool | | | | +/// | |---------------------------| |-------------| | | | +/// | service node | ... | service node | +/// |---------------------------------------------------| |--------------| /// /// A status value called "task_state" is kept in each task to indicate the task running state, /// the transition among different states are: @@ -161,7 +156,7 @@ extern __thread struct __tls_dsn__ tls_dsn; /// But for timer tasks, the state will transit to "ready" again after "running" /// as the callback need to be executed periodically. /// -/// "Data consumer" can cancel the executation of "ready" tasks with method "cancel". However, +/// The callers can cancel the execution of "ready" tasks with method "cancel". However, /// if a task is in not in "ready" state, the cancel will fail (returning false). /// /// So from the perspective of task user, a created task can only be controlled by "enqueue" or @@ -171,7 +166,7 @@ extern __thread struct __tls_dsn__ tls_dsn; /// So please take care when you call cancel. Memory leak may occur if you don't pay attention. /// For example: /// -/// int a = new int(5); +/// int *a = new int(5); /// raw_task t = new raw_task(code, [a](){ std::cout << *a << std::endl; delete a; }, hash, node); /// t->enqueue(10_seconds_latey); /// if (t->cancel()) { diff --git a/include/dsn/utility/autoref_ptr.h b/include/dsn/utility/autoref_ptr.h index 370aeafcfb..67a3f5efd6 100644 --- a/include/dsn/utility/autoref_ptr.h +++ b/include/dsn/utility/autoref_ptr.h @@ -180,6 +180,10 @@ class ref_ptr ref_ptr &operator=(ref_ptr &&obj) { + if (this == &obj) { + return *this; + } + if (nullptr != _obj) { _obj->release_ref(); } diff --git a/src/core/tests/autoref_ptr_test.cpp b/src/core/tests/autoref_ptr_test.cpp new file mode 100644 index 0000000000..1bb5abcb77 --- /dev/null +++ b/src/core/tests/autoref_ptr_test.cpp @@ -0,0 +1,574 @@ +// Copyright (c) 2012 The Chromium 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 test is copy from +// https://github.com/chromium/chromium/blob/07eea964c3f60f501782d8eb51f62ca75ddf3908/base/memory/ref_counted_unittest.cc + +#include +#include + +#include +#include + +namespace { + +class SelfAssign : public dsn::ref_counter +{ +protected: + virtual ~SelfAssign() = default; + +private: + friend class dsn::ref_counter; +}; + +class Derived : public SelfAssign +{ +protected: + ~Derived() override = default; + +private: + friend class dsn::ref_counter; +}; + +class ScopedRefPtrToSelf : public dsn::ref_counter +{ +public: + ScopedRefPtrToSelf() : self_ptr_(this) {} + + static bool was_destroyed() { return was_destroyed_; } + + static void reset_was_destroyed() { was_destroyed_ = false; } + + dsn::ref_ptr self_ptr_; + +private: + friend class dsn::ref_counter; + ~ScopedRefPtrToSelf() { was_destroyed_ = true; } + + static bool was_destroyed_; +}; + +bool ScopedRefPtrToSelf::was_destroyed_ = false; + +class ScopedRefPtrCountBase : public dsn::ref_counter +{ +public: + ScopedRefPtrCountBase() { ++constructor_count_; } + + static int constructor_count() { return constructor_count_; } + + static int destructor_count() { return destructor_count_; } + + static void reset_count() + { + constructor_count_ = 0; + destructor_count_ = 0; + } + +protected: + virtual ~ScopedRefPtrCountBase() { ++destructor_count_; } + +private: + friend class dsn::ref_counter; + + static int constructor_count_; + static int destructor_count_; +}; + +int ScopedRefPtrCountBase::constructor_count_ = 0; +int ScopedRefPtrCountBase::destructor_count_ = 0; + +class ScopedRefPtrCountDerived : public ScopedRefPtrCountBase +{ +public: + ScopedRefPtrCountDerived() { ++constructor_count_; } + + static int constructor_count() { return constructor_count_; } + + static int destructor_count() { return destructor_count_; } + + static void reset_count() + { + constructor_count_ = 0; + destructor_count_ = 0; + } + +protected: + ~ScopedRefPtrCountDerived() override { ++destructor_count_; } + +private: + friend class dsn::ref_counter; + + static int constructor_count_; + static int destructor_count_; +}; + +int ScopedRefPtrCountDerived::constructor_count_ = 0; +int ScopedRefPtrCountDerived::destructor_count_ = 0; + +class Other : public dsn::ref_counter +{ +private: + friend class dsn::ref_counter; + + ~Other() = default; +}; + +dsn::ref_ptr Overloaded(dsn::ref_ptr other) { return other; } + +dsn::ref_ptr Overloaded(dsn::ref_ptr self_assign) { return self_assign; } + +class InitialRefCountIsOne : public dsn::ref_counter +{ +public: + InitialRefCountIsOne() = default; + +private: + friend class dsn::ref_counter; + ~InitialRefCountIsOne() = default; +}; + +} // end namespace + +TEST(RefCountedUnitTest, TestSelfAssignment) +{ + SelfAssign *p = new SelfAssign; + dsn::ref_ptr var(p); + var = var; + EXPECT_EQ(var.get(), p); + var = std::move(var); + EXPECT_EQ(var.get(), p); + + // please uncomment these lines when swap are supported in ref_ptr + // var.swap(var); + // EXPECT_EQ(var.get(), p); + // swap(var, var); + // EXPECT_EQ(var.get(), p); +} + +TEST(RefCountedUnitTest, ScopedRefPtrToSelfPointerAssignment) +{ + ScopedRefPtrToSelf::reset_was_destroyed(); + + ScopedRefPtrToSelf *check = new ScopedRefPtrToSelf(); + EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed()); + check->self_ptr_ = nullptr; + EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed()); +} + +TEST(RefCountedUnitTest, ScopedRefPtrToSelfMoveAssignment) +{ + ScopedRefPtrToSelf::reset_was_destroyed(); + + ScopedRefPtrToSelf *check = new ScopedRefPtrToSelf(); + EXPECT_FALSE(ScopedRefPtrToSelf::was_destroyed()); + // Releasing |check->self_ptr_| will delete |check|. + // The move assignment operator must assign |check->self_ptr_| first then + // release |check->self_ptr_|. + check->self_ptr_ = dsn::ref_ptr(); + EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed()); +} + +TEST(RefCountedUnitTest, BooleanTesting) +{ + dsn::ref_ptr ptr_to_an_instance = new SelfAssign; + EXPECT_TRUE(ptr_to_an_instance); + EXPECT_FALSE(!ptr_to_an_instance); + + if (ptr_to_an_instance) { + } else { + ADD_FAILURE() << "Pointer to an instance should result in true."; + } + + if (!ptr_to_an_instance) { // check for operator!(). + ADD_FAILURE() << "Pointer to an instance should result in !x being false."; + } + + dsn::ref_ptr null_ptr; + EXPECT_FALSE(null_ptr); + EXPECT_TRUE(!null_ptr); + + if (null_ptr) { + ADD_FAILURE() << "Null pointer should result in false."; + } + + if (!null_ptr) { // check for operator!(). + } else { + ADD_FAILURE() << "Null pointer should result in !x being true."; + } +} + +TEST(RefCountedUnitTest, Equality) +{ + dsn::ref_ptr p1(new SelfAssign); + dsn::ref_ptr p2(new SelfAssign); + + EXPECT_EQ(p1, p1); + EXPECT_EQ(p2, p2); + + EXPECT_NE(p1, p2); + EXPECT_NE(p2, p1); +} + +TEST(RefCountedUnitTest, NullptrEquality) +{ + dsn::ref_ptr ptr_to_an_instance(new SelfAssign); + dsn::ref_ptr ptr_to_nullptr; + + EXPECT_NE(nullptr, ptr_to_an_instance); + EXPECT_NE(ptr_to_an_instance, nullptr); + EXPECT_EQ(nullptr, ptr_to_nullptr); + EXPECT_EQ(ptr_to_nullptr, nullptr); +} + +TEST(RefCountedUnitTest, ConvertibleEquality) +{ + dsn::ref_ptr p1(new Derived); + dsn::ref_ptr p2; + + EXPECT_NE(p1, p2); + EXPECT_NE(p2, p1); + + p2 = p1; + + EXPECT_EQ(p1, p2); + EXPECT_EQ(p2, p1); +} + +TEST(RefCountedUnitTest, MoveAssignment1) +{ + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + dsn::ref_ptr p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr p2; + + p2 = std::move(p1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignment2) +{ + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + dsn::ref_ptr p1; + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr p2(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p1 = std::move(p2); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(raw, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentSameInstance1) +{ + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + dsn::ref_ptr p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr p2(p1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p1 = std::move(p2); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(raw, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentSameInstance2) +{ + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + dsn::ref_ptr p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr p2(p1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p2 = std::move(p1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentDifferentInstances) +{ + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase(); + dsn::ref_ptr p1(raw1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + ScopedRefPtrCountBase *raw2 = new ScopedRefPtrCountBase(); + dsn::ref_ptr p2(raw2); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p1 = std::move(p2); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(raw2, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentSelfMove) +{ + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase; + dsn::ref_ptr p1(raw); + dsn::ref_ptr &p1_ref = p1; + + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + p1 = std::move(p1_ref); + + // |p1| is "valid but unspecified", so don't bother inspecting its + // contents, just ensure that we don't crash. + } + + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveAssignmentDerived) +{ + ScopedRefPtrCountBase::reset_count(); + ScopedRefPtrCountDerived::reset_count(); + + { + ScopedRefPtrCountBase *raw1 = new ScopedRefPtrCountBase(); + dsn::ref_ptr p1(raw1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + { + ScopedRefPtrCountDerived *raw2 = new ScopedRefPtrCountDerived(); + dsn::ref_ptr p2(raw2); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + p1 = std::move(p2); + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + EXPECT_EQ(raw2, p1.get()); + EXPECT_EQ(nullptr, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(2, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(2, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveConstructor) +{ + ScopedRefPtrCountBase::reset_count(); + + { + ScopedRefPtrCountBase *raw = new ScopedRefPtrCountBase(); + dsn::ref_ptr p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr p2(std::move(p1)); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); +} + +TEST(RefCountedUnitTest, MoveConstructorDerived) +{ + ScopedRefPtrCountBase::reset_count(); + ScopedRefPtrCountDerived::reset_count(); + + { + ScopedRefPtrCountDerived *raw1 = new ScopedRefPtrCountDerived(); + dsn::ref_ptr p1(raw1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + + { + dsn::ref_ptr p2(std::move(p1)); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountDerived::destructor_count()); + EXPECT_EQ(nullptr, p1.get()); + EXPECT_EQ(raw1, p2.get()); + + // p2 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); + + // p1 goes out of scope. + } + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountBase::destructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::constructor_count()); + EXPECT_EQ(1, ScopedRefPtrCountDerived::destructor_count()); +} + +TEST(RefCountedUnitTest, TestOverloadResolutionCopy) +{ + const dsn::ref_ptr derived(new Derived); + const dsn::ref_ptr expected(derived); + EXPECT_EQ(expected, Overloaded((dsn::ref_ptr)(derived))); + + const dsn::ref_ptr other(new Other); + EXPECT_EQ(other, Overloaded((dsn::ref_ptr)other)); +} + +TEST(RefCountedUnitTest, TestOverloadResolutionMove) +{ + dsn::ref_ptr derived(new Derived); + const dsn::ref_ptr expected(derived); + EXPECT_EQ(expected, Overloaded((dsn::ref_ptr)(std::move(derived)))); + + dsn::ref_ptr other(new Other); + const dsn::ref_ptr other2(other); + EXPECT_EQ(other2, Overloaded((dsn::ref_ptr)(std::move(other)))); +} + +TEST(RefCountedUnitTest, TestMakeRefCounted) +{ + dsn::ref_ptr derived = new Derived; + EXPECT_TRUE(derived->get_count() == 1); + derived = nullptr; + + dsn::ref_ptr derived2(new Derived()); + EXPECT_TRUE(derived2->get_count() == 1); + derived2 = nullptr; +} + +TEST(RefCountedUnitTest, TestInitialRefCountIsOne) +{ + dsn::ref_ptr obj(new InitialRefCountIsOne()); + EXPECT_TRUE(obj->get_count() == 1); + obj = nullptr; + + dsn::ref_ptr obj2(new InitialRefCountIsOne); + EXPECT_TRUE(obj2->get_count() == 1); + obj2 = nullptr; + + dsn::ref_ptr obj3(new Other()); + EXPECT_TRUE(obj3->get_count() == 1); + obj3 = nullptr; +}