1 /*
2  *  Copyright (c) 2021, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file includes definitions for a retain (reference counted) smart pointer.
32  */
33 
34 #ifndef RETAIN_PTR_HPP_
35 #define RETAIN_PTR_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #include "common/ptr_wrapper.hpp"
40 
41 namespace ot {
42 
43 /**
44  * Represents a retain (reference counted) smart pointer.
45  *
46  * The `Type` class MUST provide mechanism to track its current retain count. It MUST provide the following three
47  * methods:
48  *
49  *   - void      IncrementRetainCount(void);  (Increment the retain count).
50  *   - uint16_t  DecrementRetainCount(void);  (Decrement the retain count and return the value after decrement).
51  *   - void      Free(void);                  (Free the `Type` instance).
52  *
53  * The `Type` can inherit from `RetainCountable` which provides the retain counting methods.
54  *
55  * @tparam Type  The pointer type.
56  *
57  */
58 template <class Type> class RetainPtr : public Ptr<Type>
59 {
60     using Ptr<Type>::mPointer;
61 
62 public:
63     /**
64      * This is the default constructor for `RetainPtr` initializing it as null.
65      *
66      */
67     RetainPtr(void) = default;
68 
69     /**
70      * Initializes the `RetainPtr` with a given pointer.
71      *
72      * Upon construction the `RetainPtr` will increment the retain count on @p aPointer (if not null).
73      *
74      * @param[in] aPointer  A pointer to object to initialize with.
75      *
76      */
RetainPtr(Type * aPointer)77     explicit RetainPtr(Type *aPointer)
78         : Ptr<Type>(aPointer)
79     {
80         IncrementRetainCount();
81     }
82 
83     /**
84      * Initializes the `RetainPtr` from another `RetainPtr`.
85      *
86      * @param[in] aOther   Another `RetainPtr`.
87      *
88      */
RetainPtr(const RetainPtr & aOther)89     RetainPtr(const RetainPtr &aOther)
90         : Ptr<Type>(aOther.mPointer)
91     {
92         IncrementRetainCount();
93     }
94 
95     /**
96      * This is the destructor for `RetainPtr`.
97      *
98      * Upon destruction, the `RetainPtr` will decrement the retain count on the managed object (if not null) and
99      * free the object if its retain count reaches zero.
100      *
101      */
~RetainPtr(void)102     ~RetainPtr(void) { DecrementRetainCount(); }
103 
104     /**
105      * Replaces the managed object by `RetainPtr` with a new one.
106      *
107      * The method correctly handles a self `Reset()` (i.e., @p aPointer being the same pointer as the one currently
108      * managed by `RetainPtr`).
109      *
110      * @param[in] aPointer   A pointer to a new object to replace with.
111      *
112      */
Reset(Type * aPointer=nullptr)113     void Reset(Type *aPointer = nullptr)
114     {
115         if (aPointer != mPointer)
116         {
117             DecrementRetainCount();
118             mPointer = aPointer;
119             IncrementRetainCount();
120         }
121     }
122 
123     /**
124      * Releases the ownership of the current pointer in `RetainPtr` (if any) without changing its retain
125      * count.
126      *
127      * After this call, the `RetainPtr` will be null.
128      *
129      * @returns The pointer to the object managed by `RetainPtr` or `nullptr` if `RetainPtr` was null.
130      *
131      */
Release(void)132     Type *Release(void)
133     {
134         Type *pointer = mPointer;
135         mPointer      = nullptr;
136         return pointer;
137     }
138 
139     /**
140      * Overloads the assignment operator `=`.
141      *
142      * The `RetainPtr` first frees its current managed object (if there is any and it is different from @p aOther)
143      * before taking over the ownership of the object from @p aOther. This method correctly handles a self assignment
144      * (i.e., assigning the pointer to itself).
145      *
146      * @param[in] aOther   A reference to another `RetainPtr`.
147      *
148      * @returns A reference to this `RetainPtr`.
149      *
150      */
operator =(const RetainPtr & aOther)151     RetainPtr &operator=(const RetainPtr &aOther)
152     {
153         Reset(aOther.mPointer);
154         return *this;
155     }
156 
157 private:
IncrementRetainCount(void)158     void IncrementRetainCount(void)
159     {
160         if (mPointer != nullptr)
161         {
162             mPointer->IncrementRetainCount();
163         }
164     }
165 
DecrementRetainCount(void)166     void DecrementRetainCount(void)
167     {
168         if ((mPointer != nullptr) && (mPointer->DecrementRetainCount() == 0))
169         {
170             mPointer->Free();
171         }
172     }
173 };
174 
175 /**
176  * Provides mechanism to track retain count.
177  *
178  */
179 class RetainCountable
180 {
181     template <class Type> friend class RetainPtr;
182 
183 protected:
184     /**
185      * This constrictor initializes the object starting with retain count of zero.
186      *
187      */
RetainCountable(void)188     RetainCountable(void)
189         : mRetainCount(0)
190     {
191     }
192 
193     /**
194      * Returns the current retain count.
195      *
196      * @returns The current retain count.
197      *
198      */
GetRetainCount(void) const199     uint16_t GetRetainCount(void) const { return mRetainCount; }
200 
201     /**
202      * Increments the retain count.
203      *
204      */
IncrementRetainCount(void)205     void IncrementRetainCount(void) { ++mRetainCount; }
206 
207     /**
208      * Decrements the retain count.
209      *
210      * @returns The retain count value after decrementing it.
211      *
212      */
DecrementRetainCount(void)213     uint16_t DecrementRetainCount(void) { return --mRetainCount; }
214 
215 private:
216     uint16_t mRetainCount;
217 };
218 
219 } // namespace ot
220 
221 #endif // RETAIN_PTR_HPP_
222