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<void()> task_handler; + +/// A callback to handle rpc requests. +/// +/// Parameters: +/// - dsn_message_t: the received rpc request typedef std::function<void(dsn_message_t)> 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<void(dsn::error_code, dsn_message_t, dsn_message_t)> rpc_response_handler; + +/// Parameters: +/// - error_code +/// - size_t: the read or written size of bytes from file. typedef std::function<void(dsn::error_code, size_t)> 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 TCallback> typename std::enable_if<is_typed_rpc_callback<TCallback>::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<TCallback>::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<T> &operator=(ref_ptr<T> &&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 <type_traits> +#include <utility> + +#include <gtest/gtest.h> +#include <dsn/utility/autoref_ptr.h> + +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<ScopedRefPtrToSelf> 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<Other> Overloaded(dsn::ref_ptr<Other> other) { return other; } + +dsn::ref_ptr<SelfAssign> Overloaded(dsn::ref_ptr<SelfAssign> 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<SelfAssign> 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<ScopedRefPtrToSelf>(); + EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed()); +} + +TEST(RefCountedUnitTest, BooleanTesting) +{ + dsn::ref_ptr<SelfAssign> 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<SelfAssign> 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<SelfAssign> p1(new SelfAssign); + dsn::ref_ptr<SelfAssign> 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<SelfAssign> ptr_to_an_instance(new SelfAssign); + dsn::ref_ptr<SelfAssign> 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<Derived> p1(new Derived); + dsn::ref_ptr<SelfAssign> 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<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr<ScopedRefPtrCountBase> 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<ScopedRefPtrCountBase> p1; + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr<ScopedRefPtrCountBase> 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<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr<ScopedRefPtrCountBase> 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<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr<ScopedRefPtrCountBase> 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<ScopedRefPtrCountBase> p1(raw1); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + ScopedRefPtrCountBase *raw2 = new ScopedRefPtrCountBase(); + dsn::ref_ptr<ScopedRefPtrCountBase> 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<ScopedRefPtrCountBase> p1(raw); + dsn::ref_ptr<ScopedRefPtrCountBase> &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<ScopedRefPtrCountBase> 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<ScopedRefPtrCountDerived> 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<ScopedRefPtrCountBase> p1(raw); + EXPECT_EQ(1, ScopedRefPtrCountBase::constructor_count()); + EXPECT_EQ(0, ScopedRefPtrCountBase::destructor_count()); + + { + dsn::ref_ptr<ScopedRefPtrCountBase> 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<ScopedRefPtrCountDerived> 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<ScopedRefPtrCountBase> 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> derived(new Derived); + const dsn::ref_ptr<SelfAssign> expected(derived); + EXPECT_EQ(expected, Overloaded((dsn::ref_ptr<SelfAssign>)(derived))); + + const dsn::ref_ptr<Other> other(new Other); + EXPECT_EQ(other, Overloaded((dsn::ref_ptr<Other>)other)); +} + +TEST(RefCountedUnitTest, TestOverloadResolutionMove) +{ + dsn::ref_ptr<Derived> derived(new Derived); + const dsn::ref_ptr<SelfAssign> expected(derived); + EXPECT_EQ(expected, Overloaded((dsn::ref_ptr<SelfAssign>)(std::move(derived)))); + + dsn::ref_ptr<Other> other(new Other); + const dsn::ref_ptr<Other> other2(other); + EXPECT_EQ(other2, Overloaded((dsn::ref_ptr<Other>)(std::move(other)))); +} + +TEST(RefCountedUnitTest, TestMakeRefCounted) +{ + dsn::ref_ptr<Derived> derived = new Derived; + EXPECT_TRUE(derived->get_count() == 1); + derived = nullptr; + + dsn::ref_ptr<Derived> derived2(new Derived()); + EXPECT_TRUE(derived2->get_count() == 1); + derived2 = nullptr; +} + +TEST(RefCountedUnitTest, TestInitialRefCountIsOne) +{ + dsn::ref_ptr<InitialRefCountIsOne> obj(new InitialRefCountIsOne()); + EXPECT_TRUE(obj->get_count() == 1); + obj = nullptr; + + dsn::ref_ptr<InitialRefCountIsOne> obj2(new InitialRefCountIsOne); + EXPECT_TRUE(obj2->get_count() == 1); + obj2 = nullptr; + + dsn::ref_ptr<Other> obj3(new Other()); + EXPECT_TRUE(obj3->get_count() == 1); + obj3 = nullptr; +}