diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index ef0ee1eb6..0be95fe41 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -409,10 +409,12 @@ openthread_core_files = [ "common/notifier.cpp", "common/notifier.hpp", "common/numeric_limits.hpp", + "common/owned_ptr.hpp", "common/pool.hpp", "common/random.hpp", "common/random_manager.cpp", "common/random_manager.hpp", + "common/retain_ptr.hpp", "common/settings.cpp", "common/settings.hpp", "common/settings_driver.hpp", diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 7c1246edb..d91114a5e 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -443,9 +443,11 @@ HEADERS_COMMON = \ common/non_copyable.hpp \ common/notifier.hpp \ common/numeric_limits.hpp \ + common/owned_ptr.hpp \ common/pool.hpp \ common/random.hpp \ common/random_manager.hpp \ + common/retain_ptr.hpp \ common/settings.hpp \ common/settings_driver.hpp \ common/string.hpp \ diff --git a/src/core/common/owned_ptr.hpp b/src/core/common/owned_ptr.hpp new file mode 100644 index 000000000..88721b32d --- /dev/null +++ b/src/core/common/owned_ptr.hpp @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2021, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for an owned smart pointer. + */ + +#ifndef OWNED_PTR_HPP_ +#define OWNED_PTR_HPP_ + +#include "openthread-core-config.h" + +#include +#include + +namespace ot { + +/** + * This template class represents an owned smart pointer. + * + * `OwnedPtr` acts as sole owner of the object it manages. An `OwnedPtr` is non-copyable (copy constructor is deleted) + * but the ownership can be transferred from one `OwnedPtr` to another using move semantics. + * + * The `Type` class MUST provide `Free()` method which frees the instance. + * + * @tparam Type The pointer type. + * + */ +template class OwnedPtr +{ +public: + /** + * This is the default constructor for `OwnedPtr` initializing it as null. + * + */ + OwnedPtr(void) + : mPointer(nullptr) + { + } + + /** + * This constructor initializes the `OwnedPtr` with a given pointer. + * + * The `OwnedPtr` takes the ownership of the object at @p aPointer. + * + * @param[in] aPointer A pointer to object to initialize with. + * + */ + explicit OwnedPtr(Type *aPointer) + : mPointer(aPointer) + { + } + + /** + * This constructor initializes the `OwnedPtr` from another `OwnedPtr` using move semantics. + * + * The `OwnedPtr` takes over the ownership of the object from @p aOther. After this call, @p aOther will be null. + * + * @param[in] aOther An rvalue reference to another `OwnedPtr`. + * + */ + OwnedPtr(OwnedPtr &&aOther) + { + mPointer = aOther.mPointer; + aOther.mPointer = nullptr; + } + + /** + * This is the destructor for `OwnedPtr`. + * + * Upon destruction, the `OwnedPtr` invokes `Free()` method on its managed object (if any). + * + */ + ~OwnedPtr(void) { Delete(); } + + /** + * This method indicates whether the `OwnedPtr` is null or not. + * + * @retval TRUE The `OwnedPtr` is null. + * @retval FALSE The `OwnedPtr` is not null. + * + */ + bool IsNull(void) const { return (mPointer == nullptr); } + + /** + * This method gets the raw pointer to the object owned by `OwnedPtr`. + * + * @returns The raw pointer to the object owned by `OwnedPtr` or `nullptr` if none. + * + */ + Type *Get(void) { return mPointer; } + + /** + * This method gets the raw pointer to the object owned by `OwnedPtr`. + * + * @returns The raw pointer to the object owned by `OwnedPtr` or `nullptr` if none. + * + */ + const Type *Get(void) const { return mPointer; } + + /** + * This method frees the owned object (if any). + * + * This method invokes `Free()` method on the `Type` object owned by `OwnedPtr` (if any). It will also set the + * `OwnedPtr` to null. + * + */ + void Free(void) + { + Delete(); + mPointer = nullptr; + } + + /** + * This method frees the current object owned by `OwnedPtr` (if any) and replaces it with a new one. + * + * The method will `Free()` the current object managed by `OwnedPtr` (if different from @p aPointer) before taking + * the ownership of the object at @p aPointer. The method correctly handles a self `Reset()` (i.e., @p aPointer + * being the same pointer as the one currently managed by `OwnedPtr`). + * + * @param[in] aPointer A pointer to the new object to replace with. + * + */ + void Reset(Type *aPointer = nullptr) + { + if (mPointer != aPointer) + { + Delete(); + mPointer = aPointer; + } + } + + /** + * This method releases the ownership of the current object in `OwnedPtr` (if any). + * + * After this call, the `OwnedPtr` will be null. + * + * @returns The pointer to the object owned by `OwnedPtr` or `nullptr` if `OwnedPtr` was null. + * + */ + Type *Release(void) + { + Type *pointer = mPointer; + mPointer = nullptr; + return pointer; + } + + /** + * This method overloads the `->` dereference operator and returns a pointer to the object owned by `OwnedPtr`. + * + * @returns A pointer to owned object or `nullptr` if none. + * + */ + Type *operator->(void) { return mPointer; } + + /** + * This method overloads the `->` dereference operator and returns a pointer to the object owned by `OwnedPtr`. + * + * @returns A pointer to owned object or `nullptr` if none. + * + */ + const Type *operator->(void)const { return mPointer; } + + /** + * This method overloads the `*` dereference operator and returns a reference to the object owned by `OwnedPtr`. + * + * The behavior is undefined if `IsNull() == true`. + * + * @returns A reference to the object owned by `OwnedPtr`. + * + */ + Type &operator*(void) { return *mPointer; } + + /** + * This method overloads the `*` dereference operator and returns a reference to the object owned by `OwnedPtr`. + * + * The behavior is undefined if `IsNull() == true`. + * + * @returns A reference to the object owned by `OwnedPtr`. + * + */ + const Type &operator*(void)const { return *mPointer; } + + /** + * This method overload the assignment operator `=` to replace the object owned by the `OwnedPtr` with another one + * using move semantics. + * + * The `OwnedPtr` first frees its current owned object (if there is any and it is different from @p aOther) before + * taking over the ownership of the object from @p aOther. This method correctly handles a self assignment (i.e., + * assigning the pointer to itself). + * + * @param[in] aOther An rvalue reference to an `OwnedPtr` to move from. + * + * @returns A reference to this `OwnedPtr`. + * + */ + OwnedPtr &operator=(OwnedPtr &&aOther) + { + Reset(aOther.Release()); + return *this; + } + + OwnedPtr(const OwnedPtr &) = delete; + OwnedPtr(OwnedPtr &) = delete; + OwnedPtr &operator=(const OwnedPtr &) = delete; + +private: + void Delete(void) + { + if (mPointer != nullptr) + { + mPointer->Free(); + } + } + + Type *mPointer; +}; + +} // namespace ot + +#endif // OWNED_PTR_HPP_ diff --git a/src/core/common/retain_ptr.hpp b/src/core/common/retain_ptr.hpp new file mode 100644 index 000000000..81f6816da --- /dev/null +++ b/src/core/common/retain_ptr.hpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2021, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for a retain (reference counted) smart pointer. + */ + +#ifndef RETAIN_PTR_HPP_ +#define RETAIN_PTR_HPP_ + +#include "openthread-core-config.h" + +#include +#include + +namespace ot { + +/** + * This template class represents a retain (reference counted) smart pointer. + * + * The `Type` class MUST provide mechanism to track its current retain count. It MUST provide the following three + * methods: + * + * - void IncrementRetainCount(void); (Increment the retain count). + * - uint16_t DecrementRetainCount(void); (Decrement the retain count and return the value after decrement). + * - void Free(void); (Free the `Type` instance). + * + * The `Type` can inherit from `RetainCountable` which provides the retain counting methods. + * + * @tparam Type The pointer type. + * + */ +template class RetainPtr +{ +public: + /** + * This is the default constructor for `RetainPtr` initializing it as null. + * + */ + RetainPtr(void) + : mPointer(nullptr) + { + } + + /** + * This constructor initializes the `RetainPtr` with a given pointer. + * + * Upon construction the `RetainPtr` will increment the retain count on @p aPointer (if not null). + * + * @param[in] aPointer A pointer to object to initialize with. + * + */ + explicit RetainPtr(Type *aPointer) + : mPointer(aPointer) + { + IncrementRetainCount(); + } + + /** + * This constructor initializes the `RetainPtr` from another `RetainPtr`. + * + * @param[in] aOther Another `RetainPtr`. + * + */ + RetainPtr(const RetainPtr &aOther) + : mPointer(aOther.mPointer) + { + IncrementRetainCount(); + } + + /** + * This is the destructor for `RetainPtr`. + * + * Upon destruction, the `RetainPtr` will decrement the retain count on the managed object (if not null) and + * free the object if its retain count reaches zero. + * + */ + ~RetainPtr(void) { DecrementRetainCount(); } + + /** + * This method indicates whether the `RetainPtr` is null or not. + * + * @retval TRUE The `RetainPtr` is null. + * @retval FALSE The `RetainPtr` is not null. + * + */ + bool IsNull(void) const { return (mPointer == nullptr); } + + /** + * This method gets the raw pointer to the object managed by `RetainPtr`. + * + * @returns The raw pointer to the object managed by `RetainPtr` or `nullptr` if none. + * + */ + Type *Get(void) { return mPointer; } + + /** + * This method gets the raw pointer to the object managed by `RetainPtr`. + * + * @returns The raw pointer to the object managed by `RetainPtr` or `nullptr` if none. + * + */ + const Type *Get(void) const { return mPointer; } + + /** + * This method replaces the managed object by `RetainPtr` with a new one. + * + * The method correctly handles a self `Reset()` (i.e., @p aPointer being the same pointer as the one currently + * managed by `RetainPtr`). + * + * @param[in] aPointer A pointer to a new object to replace with. + * + */ + void Reset(Type *aPointer = nullptr) + { + if (aPointer != mPointer) + { + DecrementRetainCount(); + mPointer = aPointer; + IncrementRetainCount(); + } + } + + /** + * This method releases the ownership of the current pointer in `RetainPtr` (if any) without changing its retain + * count. + * + * After this call, the `RetainPtr` will be null. + * + * @returns The pointer to the object managed by `RetainPtr` or `nullptr` if `RetainPtr` was null. + * + */ + Type *Release(void) + { + Type *pointer = mPointer; + mPointer = nullptr; + return pointer; + } + + /** + * This method overloads the `->` dereference operator and returns a pointer to the object managed by `RetainPtr`. + * + * @returns A pointer to managed object or `nullptr` if none. + * + */ + Type *operator->(void) { return mPointer; } + + /** + * This method overloads the `->` dereference operator and returns a pointer to the object managed by `RetainPtr`. + * + * @returns A pointer to managed object or `nullptr` if none. + * + */ + const Type *operator->(void)const { return mPointer; } + + /** + * This method overloads the `*` dereference operator and returns a reference to the object managed by `RetainPtr`. + * + * The behavior is undefined if `IsNull() == true`. + * + * @returns A reference to the object managed by `RetainPtr`. + * + */ + Type &operator*(void) { return *mPointer; } + + /** + * This method overloads the `*` dereference operator and returns a reference to the object managed by `RetainPtr`. + * + * The behavior is undefined if `IsNull() == true`. + * + * @returns A reference to the object managed by `RetainPtr`. + * + */ + const Type &operator*(void)const { return *mPointer; } + + /** + * This method overload assignment operator `=` . + * + * The `RetainPtr` first frees its current managed object (if there is any and it is different from @p aOther) + * before taking over the ownership of the object from @p aOther. This method correctly handles a self assignment + * (i.e., assigning the pointer to itself). + * + * @param[in] aOther A reference to an `OwendPtr`. + * + * @returns A reference to this `RetainPtr`. + * + */ + RetainPtr &operator=(RetainPtr &aOther) + { + Reset(aOther.Get()); + return *this; + } + +private: + void IncrementRetainCount(void) + { + if (mPointer != nullptr) + { + mPointer->IncrementRetainCount(); + } + } + + void DecrementRetainCount(void) + { + if ((mPointer != nullptr) && (mPointer->DecrementRetainCount() == 0)) + { + mPointer->Free(); + } + } + + Type *mPointer; +}; + +/** + * This class provides mechanism to track retain count. + * + */ +class RetainCountable +{ + template friend class RetainPtr; + +protected: + /** + * This constrictor initializes the object starting with retain count of zero. + * + */ + RetainCountable(void) + : mRetainCount(0) + { + } + + /** + * This method returns the current retain count. + * + * @returns The current retain count. + * + */ + uint16_t GetRetainCount(void) const { return mRetainCount; } + + /** + * This method increments the retain count. + * + */ + void IncrementRetainCount(void) { ++mRetainCount; } + + /** + * This method decrements the retain count. + * + * @returns The retain count value after decrementing it. + * + */ + uint16_t DecrementRetainCount(void) { return --mRetainCount; } + +private: + uint16_t mRetainCount; +}; + +} // namespace ot + +#endif // RETAIN_PTR_HPP_ diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 4daacfdb7..1635cd836 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -705,6 +705,27 @@ target_link_libraries(ot-test-pskc add_test(NAME ot-test-pskc COMMAND ot-test-pskc) +add_executable(ot-test-smart-ptrs + test_smart_ptrs.cpp +) + +target_include_directories(ot-test-smart-ptrs + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-smart-ptrs + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-smart-ptrs + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-smart-ptrs COMMAND ot-test-smart-ptrs) + add_executable(ot-test-meshcop test_meshcop.cpp ) diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index a91ca5fea..30026a21e 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -139,6 +139,7 @@ check_PROGRAMS += \ ot-test-pool \ ot-test-priority-queue \ ot-test-pskc \ + ot-test-smart-ptrs \ ot-test-string \ ot-test-timer \ $(NULL) @@ -277,6 +278,9 @@ ot_test_priority_queue_SOURCES = $(COMMON_SOURCES) test_priority_queue.cpp ot_test_pskc_LDADD = $(COMMON_LDADD) ot_test_pskc_SOURCES = $(COMMON_SOURCES) test_pskc.cpp +ot_test_smart_ptrs_LDADD = $(COMMON_LDADD) +ot_test_smart_ptrs_SOURCES = $(COMMON_SOURCES) test_smart_ptrs.cpp + ot_test_meshcop_LDADD = $(COMMON_LDADD) ot_test_meshcop_SOURCES = $(COMMON_SOURCES) test_meshcop.cpp diff --git a/tests/unit/test_smart_ptrs.cpp b/tests/unit/test_smart_ptrs.cpp new file mode 100644 index 000000000..5ca1c2b3f --- /dev/null +++ b/tests/unit/test_smart_ptrs.cpp @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2021, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_platform.h" + +#include + +#include + +#include "test_util.hpp" +#include "common/code_utils.hpp" +#include "common/owned_ptr.hpp" +#include "common/retain_ptr.hpp" + +namespace ot { + +class TestObject : public RetainCountable +{ +public: + TestObject(void) + : mWasFreed(false) + { + } + + void Free(void) { mWasFreed = true; } + void ResetTestFlags(void) { mWasFreed = false; } + uint16_t GetRetainCount(void) const { return RetainCountable::GetRetainCount(); } + bool WasFreed(void) const { return mWasFreed && (GetRetainCount() == 0); } + +private: + bool mWasFreed; +}; + +static constexpr uint16_t kSkipRetainCountCheck = 0xffff; + +template +void VerifyPointer(const PointerType &aPointer, + const TestObject * aObject, + uint16_t aRetainCount = kSkipRetainCountCheck) +{ + if (aObject == nullptr) + { + VerifyOrQuit(aPointer.IsNull()); + VerifyOrQuit(aPointer.Get() == nullptr); + } + else + { + VerifyOrQuit(!aPointer.IsNull()); + VerifyOrQuit(aPointer.Get() == aObject); + + VerifyOrQuit(!aPointer->WasFreed()); + VerifyOrQuit(!(*aPointer).WasFreed()); + + if (aRetainCount != kSkipRetainCountCheck) + { + VerifyOrQuit(aObject->GetRetainCount() == aRetainCount); + } + } +} + +void TestOwnedPtr(void) +{ + TestObject obj1; + TestObject obj2; + TestObject obj3; + + printf("\n====================================================================================\n"); + printf("Testing `OwnedPtr`\n"); + + printf("\n - Default constructor (null pointer)"); + + { + OwnedPtr ptr; + + VerifyPointer(ptr, nullptr); + } + + printf("\n - Constructor taking ownership of an object"); + obj1.ResetTestFlags(); + + { + OwnedPtr ptr(&obj1); + + VerifyPointer(ptr, &obj1); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - Move constructor taking over from another"); + obj1.ResetTestFlags(); + + { + OwnedPtr ptr1(&obj1); + OwnedPtr ptr2(static_cast &&>(ptr1)); + + VerifyPointer(ptr1, nullptr); + VerifyPointer(ptr2, &obj1); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - `Free()` method"); + obj1.ResetTestFlags(); + + { + OwnedPtr ptr(&obj1); + + VerifyPointer(ptr, &obj1); + + ptr.Free(); + + VerifyOrQuit(obj1.WasFreed()); + VerifyPointer(ptr, nullptr); + + ptr.Free(); + + VerifyOrQuit(obj1.WasFreed()); + VerifyPointer(ptr, nullptr); + } + + printf("\n - `Reset()` method"); + obj1.ResetTestFlags(); + obj2.ResetTestFlags(); + obj3.ResetTestFlags(); + + { + OwnedPtr ptr(&obj1); + + VerifyPointer(ptr, &obj1); + + ptr.Reset(&obj2); + + VerifyOrQuit(obj1.WasFreed()); + VerifyOrQuit(!obj2.WasFreed()); + VerifyPointer(ptr, &obj2); + + ptr.Reset(); + VerifyOrQuit(obj2.WasFreed()); + VerifyPointer(ptr, nullptr); + + ptr.Reset(&obj3); + VerifyPointer(ptr, &obj3); + } + + VerifyOrQuit(obj1.WasFreed()); + VerifyOrQuit(obj2.WasFreed()); + VerifyOrQuit(obj3.WasFreed()); + + printf("\n - Self `Reset()`"); + obj1.ResetTestFlags(); + + { + OwnedPtr ptr1(&obj1); + OwnedPtr ptr2; + + VerifyPointer(ptr1, &obj1); + + ptr1.Reset(&obj1); + VerifyPointer(ptr1, &obj1); + + ptr2.Reset(nullptr); + VerifyPointer(ptr2, nullptr); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - Move assignment (operator `=`)"); + obj1.ResetTestFlags(); + obj2.ResetTestFlags(); + obj3.ResetTestFlags(); + + { + OwnedPtr ptr1(&obj1); + OwnedPtr ptr2(&obj2); + OwnedPtr ptr3(&obj3); + + VerifyPointer(ptr1, &obj1); + VerifyPointer(ptr2, &obj2); + VerifyPointer(ptr3, &obj3); + + // Move from non-null (ptr1) to non-null (ptr2) + ptr2 = static_cast &&>(ptr1); + VerifyPointer(ptr1, nullptr); + VerifyPointer(ptr2, &obj1); + VerifyOrQuit(!obj1.WasFreed()); + VerifyOrQuit(obj2.WasFreed()); + + // Move from null (ptr1) to non-null (ptr3) + ptr3 = static_cast &&>(ptr1); + VerifyPointer(ptr1, nullptr); + VerifyPointer(ptr3, nullptr); + VerifyOrQuit(obj3.WasFreed()); + + // Move from non-null (ptr2) to null (ptr1) + ptr1 = static_cast &&>(ptr2); + VerifyPointer(ptr1, &obj1); + VerifyPointer(ptr2, nullptr); + VerifyOrQuit(!obj1.WasFreed()); + + // Move from null (ptr2) to null (ptr3) + ptr3 = static_cast &&>(ptr2); + VerifyPointer(ptr2, nullptr); + VerifyPointer(ptr3, nullptr); + VerifyOrQuit(!obj1.WasFreed()); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - Self move assignment (operator `=`)"); + obj1.ResetTestFlags(); + + { + OwnedPtr ptr1(&obj1); + OwnedPtr ptr2; + + VerifyPointer(ptr1, &obj1); + VerifyPointer(ptr2, nullptr); + + // Move from non-null (ptr1) to itself + ptr1 = static_cast &&>(ptr1); + VerifyPointer(ptr1, &obj1); + + // Move from null (ptr2) to itself + ptr2 = static_cast &&>(ptr2); + VerifyPointer(ptr2, nullptr); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - `Release()` method"); + obj1.ResetTestFlags(); + + { + OwnedPtr ptr(&obj1); + + VerifyPointer(ptr, &obj1); + + VerifyOrQuit(ptr.Release() == &obj1); + VerifyOrQuit(!obj1.WasFreed()); + VerifyPointer(ptr, nullptr); + + VerifyOrQuit(ptr.Release() == nullptr); + VerifyOrQuit(!obj1.WasFreed()); + VerifyPointer(ptr, nullptr); + } + + printf("\n\n-- PASS\n"); +} + +void TestRetainPtr(void) +{ + TestObject obj1; + TestObject obj2; + TestObject obj3; + + printf("\n====================================================================================\n"); + printf("Testing `RetainPtr`\n"); + + VerifyOrQuit(obj1.GetRetainCount() == 0); + VerifyOrQuit(obj2.GetRetainCount() == 0); + VerifyOrQuit(obj3.GetRetainCount() == 0); + + printf("\n - Default constructor (null pointer)"); + + { + RetainPtr ptr; + + VerifyPointer(ptr, nullptr); + } + + printf("\n - Constructor taking over management of an object"); + obj1.ResetTestFlags(); + + { + RetainPtr ptr(&obj1); + + VerifyPointer(ptr, &obj1, 1); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - Two constructed `RetainPtr`s of the same object"); + obj1.ResetTestFlags(); + + { + RetainPtr ptr1(&obj1); + RetainPtr ptr2(&obj1); + + VerifyPointer(ptr1, &obj1, 2); + VerifyPointer(ptr2, &obj1, 2); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - Copy constructor"); + obj1.ResetTestFlags(); + + { + RetainPtr ptr1(&obj1); + RetainPtr ptr2(ptr1); + + VerifyPointer(ptr1, &obj1, 2); + VerifyPointer(ptr2, &obj1, 2); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - `Reset()` method"); + obj1.ResetTestFlags(); + obj2.ResetTestFlags(); + obj3.ResetTestFlags(); + + { + RetainPtr ptr(&obj1); + + VerifyPointer(ptr, &obj1, 1); + + ptr.Reset(&obj2); + VerifyOrQuit(obj1.WasFreed()); + VerifyOrQuit(!obj2.WasFreed()); + VerifyPointer(ptr, &obj2, 1); + + ptr.Reset(); + VerifyOrQuit(obj2.WasFreed()); + VerifyPointer(ptr, nullptr); + + ptr.Reset(&obj3); + VerifyPointer(ptr, &obj3, 1); + } + + VerifyOrQuit(obj1.WasFreed()); + VerifyOrQuit(obj2.WasFreed()); + VerifyOrQuit(obj3.WasFreed()); + + printf("\n - Self `Reset()`"); + obj1.ResetTestFlags(); + + { + RetainPtr ptr1(&obj1); + RetainPtr ptr2; + + VerifyPointer(ptr1, &obj1, 1); + + ptr1.Reset(&obj1); + VerifyPointer(ptr1, &obj1, 1); + + ptr2.Reset(nullptr); + VerifyPointer(ptr2, nullptr); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - Assignment `=`"); + obj1.ResetTestFlags(); + obj2.ResetTestFlags(); + + { + RetainPtr ptr1(&obj1); + RetainPtr ptr2(&obj2); + RetainPtr ptr3; + + VerifyPointer(ptr1, &obj1, 1); + VerifyPointer(ptr2, &obj2, 1); + VerifyPointer(ptr3, nullptr); + + // Set from non-null (ptr1) to non-null (ptr2) + ptr2 = ptr1; + VerifyPointer(ptr1, &obj1, 2); + VerifyPointer(ptr2, &obj1, 2); + VerifyOrQuit(obj2.WasFreed()); + + // Set from null (ptr3) to non-null (ptr1) + ptr1 = ptr3; + VerifyPointer(ptr1, nullptr); + VerifyPointer(ptr3, nullptr); + VerifyPointer(ptr2, &obj1, 1); + + // Move from null (ptr1) to null (ptr3) + ptr3 = ptr1; + VerifyPointer(ptr1, nullptr); + VerifyPointer(ptr3, nullptr); + + // Move from non-null (ptr2) to null (ptr3) + ptr3 = ptr2; + VerifyPointer(ptr2, &obj1, 2); + VerifyPointer(ptr3, &obj1, 2); + } + + VerifyOrQuit(obj1.WasFreed()); + VerifyOrQuit(obj2.WasFreed()); + + printf("\n - Self assignment `=`"); + obj1.ResetTestFlags(); + + { + RetainPtr ptr1(&obj1); + RetainPtr ptr2; + + VerifyPointer(ptr1, &obj1, 1); + VerifyPointer(ptr2, nullptr); + + // Set from non-null (ptr1) to itself. We use `*(&ptr1) to + // silence `clang` error/warning not allowing explicit self + // assignment to same variable. + ptr1 = *(&ptr1); + VerifyPointer(ptr1, &obj1, 1); + + // Set from null (ptr2) to itself + ptr2 = *(&ptr2); + VerifyPointer(ptr2, nullptr); + } + + VerifyOrQuit(obj1.WasFreed()); + + printf("\n - `Release()` method"); + obj1.ResetTestFlags(); + + { + RetainPtr ptr(&obj1); + + VerifyPointer(ptr, &obj1, 1); + + VerifyOrQuit(ptr.Release() == &obj1); + VerifyPointer(ptr, nullptr); + + VerifyOrQuit(ptr.Release() == nullptr); + VerifyPointer(ptr, nullptr); + } + + VerifyOrQuit(!obj1.WasFreed()); + VerifyOrQuit(obj1.GetRetainCount() == 1); + + printf("\n\n-- PASS\n"); +} + +} // namespace ot + +int main(void) +{ + ot::TestOwnedPtr(); + ot::TestRetainPtr(); + printf("\nAll tests passed.\n"); + return 0; +}