xref: /aosp_15_r20/external/pigweed/pw_intrusive_ptr/public/pw_intrusive_ptr/recyclable.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <type_traits>
17 
18 // pw::Recyclable<T>
19 //
20 // Notes:
21 //
22 // pw::Recyclable<T> is a mix-in class which allows users to control what
23 // happens to objects when they reach the end of their lifecycle, as determined
24 // by the Pigweed managed pointer classes.
25 //
26 // The general idea is as follows. A developer might have some sort of factory
27 // pattern where they hand out unique_ptr<>s or IntrusivePtr<>s to objects which
28 // they have created. When their user is done with the object and the managed
29 // pointers let go of it, instead of executing the destructor and deleting the
30 // object, the developer may want to "recycle" the object and use it for some
31 // internal purpose. Examples include...
32 //
33 // 1) Putting the object on some sort of internal list to hand out again
34 //    of the object is re-usable and the cost of construction/destruction
35 //    is high.
36 // 2) Putting the object into some form of deferred destruction queue
37 //    because users are either too high priority to pay the cost of
38 //    destruction when the object is released, or because the act of
39 //    destruction might involve operations which are not permitted when
40 //    the object is released (perhaps the object is released at IRQ time,
41 //    but the system needs to be running in a thread in order to properly
42 //    clean up the object)
43 // 3) Re-using the object internally for something like bookkeeping
44 //    purposes.
45 //
46 // In order to make use of the feature, users need to do two things.
47 //
48 // 1) Derive from pw::Recyclable<T>.
49 // 2) Implement a method with the signature "void pw_recycle()"
50 //
51 // When deriving from Recyclable<T>, T should be devoid of cv-qualifiers (even
52 // if the managed pointers handed out by the user's code are const or volatile).
53 // In addition, pw_recycle must be visible to pw::Recyclable<T>, either
54 // because it is public or because the T is friends with pw::Recyclable<T>.
55 //
56 // :: Example ::
57 //
58 // Some code hands out unique pointers to const Foo objects and wishes to
59 // have the chance to recycle them.  The code would look something like
60 // this...
61 //
62 // class Foo : public pw::Recyclable<Foo> {
63 // public:
64 //   // public implementation here
65 // private:
66 //   friend class pw::Recyclable<Foo>;
67 //   void pw_recycle() {
68 //     if (should_recycle())) {
69 //       do_recycle_stuff();
70 //     } else {
71 //       delete this;
72 //     }
73 //   }
74 // };
75 //
76 // Note: the intention is to use this feature with managed pointers,
77 // which will automatically detect and call the recycle method if
78 // present.  That said, there is nothing to stop users for manually
79 // calling pw_recycle, provided that it is visible to the code which
80 // needs to call it.
81 
82 namespace pw {
83 
84 // Default implementation of pw::Recyclable.
85 //
86 // Note: we provide a default implementation instead of just a fwd declaration
87 // so we can add a static_assert which will give a user a more human readable
88 // error in case they make the mistake of deriving from pw::Recyclable<const
89 // Foo> instead of pw::Recyclable<Foo>
90 template <typename T, typename = void>
91 class Recyclable {
92   // Note: static assert must depend on T in order to trigger only when the
93   // template gets expanded.  If it does not depend on any template parameters,
94   // eg static_assert(false), then it will always explode, regardless of whether
95   // or not the template is ever expanded.
96   static_assert(
97       std::is_same_v<T, T> == false,
98       "pw::Recyclable<T> objects must not specify cv-qualifiers for T.  "
99       "Derive from pw::Recyclable<Foo>, not pw::Recyclable<const Foo>");
100 };
101 
102 namespace internal {
103 
104 // Test to see if an object is recyclable.  An object of type T is considered to
105 // be recyclable if it derives from pw::Recyclable<T>
106 template <typename T>
107 inline constexpr bool has_pw_recycle_v =
108     std::is_base_of_v<::pw::Recyclable<std::remove_cv_t<T>>, T>;
109 
110 template <typename T>
recycle(T * ptr)111 inline void recycle(T* ptr) {
112   static_assert(has_pw_recycle_v<T>, "T must derive from pw::Recyclable");
113   Recyclable<std::remove_cv_t<T>>::pw_recycle_thunk(
114       const_cast<std::remove_cv_t<T>*>(ptr));
115 }
116 
117 }  // namespace internal
118 
119 template <typename T>
120 class Recyclable<T, std::enable_if_t<std::is_same_v<std::remove_cv_t<T>, T>>> {
121  private:
122   friend void ::pw::internal::recycle<T>(T*);
123   friend void ::pw::internal::recycle<const T>(const T*);
124 
pw_recycle_thunk(T * ptr)125   static void pw_recycle_thunk(T* ptr) {
126     static_assert(std::is_same_v<decltype(&T::pw_recycle), void (T::*)(void)>,
127                   "pw_recycle() methods must be non-static member functions "
128                   "with the signature 'void pw_recycle()', and be visible to "
129                   "pw::Recyclable<T> (either because they are public, or "
130                   "because of friendship).");
131     ptr->pw_recycle();
132   }
133 };
134 
135 }  // namespace pw
136