mirror of
https://github.com/espressif/openthread.git
synced 2026-08-05 10:27:49 +00:00
[common] adding OwnedPtr and RetainPtr classes (#7171)
This commit adds two smart pointer types `OwnedPtr` and `RetainPtr`. `OwnedPtr` acts as the sole owner of the object it manages. It is non-copyable but the ownership can be transferred from one `OwnedPtr` to another using move semantics. `RetainPtr` is an intrusive reference counted smart pointer allowing multiple pointers to share management of the same object. It requires the underlying `Type` object to provide mechanism to track the current retain count. This may be realized by the `Type` itself providing this or by having it be a sub-class of the newly added `RetainCountable` class. This commit also add a unit test `test_smart_ptr` validating the behavior of newly added classes.
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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 Type> 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_
|
||||
@@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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 Type> 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 <class Type> 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_
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 <string.h>
|
||||
|
||||
#include <openthread/config.h>
|
||||
|
||||
#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 <typename PointerType>
|
||||
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<TestObject> ptr;
|
||||
|
||||
VerifyPointer(ptr, nullptr);
|
||||
}
|
||||
|
||||
printf("\n - Constructor taking ownership of an object");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
OwnedPtr<TestObject> ptr(&obj1);
|
||||
|
||||
VerifyPointer(ptr, &obj1);
|
||||
}
|
||||
|
||||
VerifyOrQuit(obj1.WasFreed());
|
||||
|
||||
printf("\n - Move constructor taking over from another");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
OwnedPtr<TestObject> ptr1(&obj1);
|
||||
OwnedPtr<TestObject> ptr2(static_cast<OwnedPtr<TestObject> &&>(ptr1));
|
||||
|
||||
VerifyPointer(ptr1, nullptr);
|
||||
VerifyPointer(ptr2, &obj1);
|
||||
}
|
||||
|
||||
VerifyOrQuit(obj1.WasFreed());
|
||||
|
||||
printf("\n - `Free()` method");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
OwnedPtr<TestObject> 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<TestObject> 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<TestObject> ptr1(&obj1);
|
||||
OwnedPtr<TestObject> 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<TestObject> ptr1(&obj1);
|
||||
OwnedPtr<TestObject> ptr2(&obj2);
|
||||
OwnedPtr<TestObject> ptr3(&obj3);
|
||||
|
||||
VerifyPointer(ptr1, &obj1);
|
||||
VerifyPointer(ptr2, &obj2);
|
||||
VerifyPointer(ptr3, &obj3);
|
||||
|
||||
// Move from non-null (ptr1) to non-null (ptr2)
|
||||
ptr2 = static_cast<OwnedPtr<TestObject> &&>(ptr1);
|
||||
VerifyPointer(ptr1, nullptr);
|
||||
VerifyPointer(ptr2, &obj1);
|
||||
VerifyOrQuit(!obj1.WasFreed());
|
||||
VerifyOrQuit(obj2.WasFreed());
|
||||
|
||||
// Move from null (ptr1) to non-null (ptr3)
|
||||
ptr3 = static_cast<OwnedPtr<TestObject> &&>(ptr1);
|
||||
VerifyPointer(ptr1, nullptr);
|
||||
VerifyPointer(ptr3, nullptr);
|
||||
VerifyOrQuit(obj3.WasFreed());
|
||||
|
||||
// Move from non-null (ptr2) to null (ptr1)
|
||||
ptr1 = static_cast<OwnedPtr<TestObject> &&>(ptr2);
|
||||
VerifyPointer(ptr1, &obj1);
|
||||
VerifyPointer(ptr2, nullptr);
|
||||
VerifyOrQuit(!obj1.WasFreed());
|
||||
|
||||
// Move from null (ptr2) to null (ptr3)
|
||||
ptr3 = static_cast<OwnedPtr<TestObject> &&>(ptr2);
|
||||
VerifyPointer(ptr2, nullptr);
|
||||
VerifyPointer(ptr3, nullptr);
|
||||
VerifyOrQuit(!obj1.WasFreed());
|
||||
}
|
||||
|
||||
VerifyOrQuit(obj1.WasFreed());
|
||||
|
||||
printf("\n - Self move assignment (operator `=`)");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
OwnedPtr<TestObject> ptr1(&obj1);
|
||||
OwnedPtr<TestObject> ptr2;
|
||||
|
||||
VerifyPointer(ptr1, &obj1);
|
||||
VerifyPointer(ptr2, nullptr);
|
||||
|
||||
// Move from non-null (ptr1) to itself
|
||||
ptr1 = static_cast<OwnedPtr<TestObject> &&>(ptr1);
|
||||
VerifyPointer(ptr1, &obj1);
|
||||
|
||||
// Move from null (ptr2) to itself
|
||||
ptr2 = static_cast<OwnedPtr<TestObject> &&>(ptr2);
|
||||
VerifyPointer(ptr2, nullptr);
|
||||
}
|
||||
|
||||
VerifyOrQuit(obj1.WasFreed());
|
||||
|
||||
printf("\n - `Release()` method");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
OwnedPtr<TestObject> 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<TestObject> ptr;
|
||||
|
||||
VerifyPointer(ptr, nullptr);
|
||||
}
|
||||
|
||||
printf("\n - Constructor taking over management of an object");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
RetainPtr<TestObject> ptr(&obj1);
|
||||
|
||||
VerifyPointer(ptr, &obj1, 1);
|
||||
}
|
||||
|
||||
VerifyOrQuit(obj1.WasFreed());
|
||||
|
||||
printf("\n - Two constructed `RetainPtr`s of the same object");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
RetainPtr<TestObject> ptr1(&obj1);
|
||||
RetainPtr<TestObject> ptr2(&obj1);
|
||||
|
||||
VerifyPointer(ptr1, &obj1, 2);
|
||||
VerifyPointer(ptr2, &obj1, 2);
|
||||
}
|
||||
|
||||
VerifyOrQuit(obj1.WasFreed());
|
||||
|
||||
printf("\n - Copy constructor");
|
||||
obj1.ResetTestFlags();
|
||||
|
||||
{
|
||||
RetainPtr<TestObject> ptr1(&obj1);
|
||||
RetainPtr<TestObject> ptr2(ptr1);
|
||||
|
||||
VerifyPointer(ptr1, &obj1, 2);
|
||||
VerifyPointer(ptr2, &obj1, 2);
|
||||
}
|
||||
|
||||
VerifyOrQuit(obj1.WasFreed());
|
||||
|
||||
printf("\n - `Reset()` method");
|
||||
obj1.ResetTestFlags();
|
||||
obj2.ResetTestFlags();
|
||||
obj3.ResetTestFlags();
|
||||
|
||||
{
|
||||
RetainPtr<TestObject> 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<TestObject> ptr1(&obj1);
|
||||
RetainPtr<TestObject> 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<TestObject> ptr1(&obj1);
|
||||
RetainPtr<TestObject> ptr2(&obj2);
|
||||
RetainPtr<TestObject> 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<TestObject> ptr1(&obj1);
|
||||
RetainPtr<TestObject> 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<TestObject> 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;
|
||||
}
|
||||
Reference in New Issue
Block a user