• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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