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;
+}