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
15 #include "pw_bluetooth_sapphire/internal/host/common/weak_self.h"
16
17 #include <pw_async/fake_dispatcher_fixture.h>
18 #include <pw_async/heap_dispatcher.h>
19
20 #include "pw_unit_test/framework.h"
21
22 namespace bt {
23 namespace {
24
25 using WeakSelfTest = pw::async::test::FakeDispatcherFixture;
26
27 class FunctionTester : public WeakSelf<FunctionTester> {
28 public:
FunctionTester(uint8_t testval,pw::async::Dispatcher & pw_dispatcher)29 explicit FunctionTester(uint8_t testval, pw::async::Dispatcher& pw_dispatcher)
30 : WeakSelf(this), value_(testval), heap_dispatcher_(pw_dispatcher) {}
31
callback_later_with_weak(fit::function<void (FunctionTester::WeakPtr)> cb)32 void callback_later_with_weak(
33 fit::function<void(FunctionTester::WeakPtr)> cb) {
34 auto weak = GetWeakPtr();
35 (void)heap_dispatcher_.Post(
36 [self = std::move(weak), cb = std::move(cb)](pw::async::Context /*ctx*/,
37 pw::Status status) {
38 if (status.ok()) {
39 cb(self);
40 }
41 });
42 }
43
value() const44 uint8_t value() const { return value_; }
45
46 private:
47 uint8_t value_;
48 pw::async::HeapDispatcher heap_dispatcher_;
49 };
50
TEST_F(WeakSelfTest,InvalidatingSelf)51 TEST_F(WeakSelfTest, InvalidatingSelf) {
52 bool called = false;
53 FunctionTester::WeakPtr ptr;
54
55 // Default-constructed weak pointers are not alive.
56 EXPECT_FALSE(ptr.is_alive());
57
58 auto cb = [&ptr, &called](auto weakptr) {
59 called = true;
60 ptr = weakptr;
61 };
62
63 {
64 FunctionTester test(0xBA, dispatcher());
65
66 test.callback_later_with_weak(cb);
67
68 // Run the loop until we're called back.
69 RunUntilIdle();
70
71 EXPECT_TRUE(called);
72 EXPECT_TRUE(ptr.is_alive());
73 EXPECT_EQ(&test, &ptr.get());
74 EXPECT_EQ(0xBA, ptr->value());
75
76 called = false;
77 test.callback_later_with_weak(cb);
78
79 // Now out of scope.
80 }
81
82 // Run the loop until we're called back.
83 RunUntilIdle();
84
85 EXPECT_TRUE(called);
86 EXPECT_FALSE(ptr.is_alive());
87 EXPECT_DEATH_IF_SUPPORTED(ptr.get(), "destroyed");
88 }
89
TEST_F(WeakSelfTest,InvalidatePtrs)90 TEST_F(WeakSelfTest, InvalidatePtrs) {
91 bool called = false;
92 FunctionTester::WeakPtr ptr;
93
94 // Default-constructed weak pointers are not alive.
95 EXPECT_FALSE(ptr.is_alive());
96
97 auto cb = [&ptr, &called](auto weakptr) {
98 called = true;
99 ptr = weakptr;
100 };
101
102 FunctionTester test(0xBA, dispatcher());
103
104 test.callback_later_with_weak(cb);
105
106 // Run the loop until we're called back.
107 RunUntilIdle();
108
109 EXPECT_TRUE(called);
110 EXPECT_TRUE(ptr.is_alive());
111 EXPECT_EQ(&test, &ptr.get());
112 EXPECT_EQ(0xBA, ptr->value());
113
114 called = false;
115 test.callback_later_with_weak(cb);
116
117 // Now invalidate the pointers.
118 test.InvalidatePtrs();
119
120 // Run the loop until we're called back.
121 RunUntilIdle();
122
123 EXPECT_TRUE(called);
124 EXPECT_FALSE(ptr.is_alive());
125 EXPECT_DEATH_IF_SUPPORTED(ptr.get(), "destroyed");
126 }
127
128 class StaticTester;
129
130 class OnlyTwoStaticManager {
131 public:
OnlyTwoStaticManager(StaticTester * self_ptr)132 explicit OnlyTwoStaticManager(StaticTester* self_ptr) : obj_ptr_(self_ptr) {}
~OnlyTwoStaticManager()133 ~OnlyTwoStaticManager() { InvalidateAll(); }
134
135 using RefType = RecyclingWeakRef;
136
GetWeakRef()137 std::optional<pw::IntrusivePtr<RefType>> GetWeakRef() {
138 for (auto& ptr : OnlyTwoStaticManager::pointers_) {
139 if (ptr.is_alive() && ptr.get() == obj_ptr_) {
140 // Already adopted, add another refptr pointing to it.
141 return pw::IntrusivePtr(&ptr);
142 }
143 }
144 for (auto& ptr : OnlyTwoStaticManager::pointers_) {
145 if (!ptr.is_in_use()) {
146 return ptr.alloc(obj_ptr_);
147 }
148 }
149 return std::nullopt;
150 }
151
InvalidateAll()152 void InvalidateAll() {
153 OnlyTwoStaticManager::pointers_[0].maybe_unset(obj_ptr_);
154 OnlyTwoStaticManager::pointers_[1].maybe_unset(obj_ptr_);
155 }
156
157 private:
158 StaticTester* obj_ptr_;
159 inline static RecyclingWeakRef pointers_[2];
160 };
161
162 class StaticTester : public WeakSelf<StaticTester, OnlyTwoStaticManager> {
163 public:
StaticTester(uint8_t testval)164 explicit StaticTester(uint8_t testval) : WeakSelf(this), value_(testval) {}
165
value() const166 uint8_t value() const { return value_; }
167
168 private:
169 uint8_t value_;
170 };
171
TEST_F(WeakSelfTest,StaticRecyclingPointers)172 TEST_F(WeakSelfTest, StaticRecyclingPointers) {
173 // We can create more objects than we have weak space for.
174 StaticTester test1(1);
175 StaticTester test2(2);
176 StaticTester test3(3);
177
178 // And create as many weak pointers of one of them as we want.
179 auto ptr = test1.GetWeakPtr();
180 auto ptr2 = test1.GetWeakPtr();
181 auto ptr3 = test1.GetWeakPtr();
182 auto ptr4 = ptr;
183
184 // Make the second one have some ptrs too.
185 {
186 {
187 StaticTester test4(4);
188 auto second_ptr = test4.GetWeakPtr();
189 auto second_ptr2 = test4.GetWeakPtr();
190 EXPECT_TRUE(ptr4.is_alive());
191 StaticTester* ptr4_old = &ptr4.get();
192 ptr4 = second_ptr;
193 EXPECT_TRUE(ptr4.is_alive());
194 // It's moved to the new one though.
195 EXPECT_NE(&ptr4.get(), ptr4_old);
196 EXPECT_EQ(&ptr4.get(), &test4);
197 }
198 // ptr4 outlived it's target.
199 EXPECT_FALSE(ptr4.is_alive());
200 // Now let's make the second weak pointer unused, recycling it.
201 ptr4 = ptr3;
202 }
203
204 // Now I can get a second weak ptr still, from our third object.
205 auto still_okay = test3.GetWeakPtr();
206 auto still_copy = still_okay;
207 EXPECT_TRUE(still_copy.is_alive());
208 }
209
TEST_F(WeakSelfTest,StaticDeathWhenExhausted)210 TEST_F(WeakSelfTest, StaticDeathWhenExhausted) {
211 StaticTester test1(1);
212 StaticTester test3(3);
213
214 auto ptr1 = test1.GetWeakPtr();
215 auto ptr2 = ptr1;
216 {
217 StaticTester test2(2);
218
219 ptr2 = test2.GetWeakPtr();
220
221 EXPECT_TRUE(ptr2.is_alive());
222 EXPECT_TRUE(ptr1.is_alive());
223 }
224
225 EXPECT_FALSE(ptr2.is_alive());
226
227 EXPECT_DEATH_IF_SUPPORTED(test3.GetWeakPtr(), ".*");
228 }
229
230 class BaseClass {
231 public:
232 BaseClass() = default;
233 virtual ~BaseClass() = default;
234
set_value(int value)235 void set_value(int value) { value_ = value; }
236
value() const237 int value() const { return value_; }
238
239 private:
240 int value_ = 0;
241 };
242
243 class ChildClass : public BaseClass, public WeakSelf<ChildClass> {
244 public:
ChildClass()245 ChildClass() : BaseClass(), WeakSelf<ChildClass>(this) {}
246 };
247
TEST_F(WeakSelfTest,Upcast)248 TEST_F(WeakSelfTest, Upcast) {
249 ChildClass obj;
250
251 WeakPtr<ChildClass> child_weak = obj.GetWeakPtr();
252 child_weak->set_value(1);
253 EXPECT_EQ(child_weak->value(), 1);
254
255 WeakPtr<BaseClass> base_weak_copy(child_weak);
256 EXPECT_TRUE(child_weak.is_alive());
257 base_weak_copy->set_value(2);
258 EXPECT_EQ(base_weak_copy->value(), 2);
259
260 WeakPtr<BaseClass> base_weak_move(std::move(child_weak));
261 EXPECT_FALSE(child_weak.is_alive());
262 base_weak_move->set_value(3);
263 EXPECT_EQ(base_weak_move->value(), 3);
264 }
265
266 } // namespace
267 } // namespace bt
268