blob: 214209cba18225dac4ecd19c72a0b544fdf1a635 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/weak_self.h"
#include <gtest/gtest.h>
#include <pw_async/fake_dispatcher_fixture.h>
#include <pw_async/heap_dispatcher.h>
namespace bt {
namespace {
using WeakSelfTest = pw::async::test::FakeDispatcherFixture;
class FunctionTester : public WeakSelf<FunctionTester> {
public:
explicit FunctionTester(uint8_t testval, pw::async::Dispatcher& pw_dispatcher)
: WeakSelf(this), value_(testval), heap_dispatcher_(pw_dispatcher) {}
void callback_later_with_weak(
fit::function<void(FunctionTester::WeakPtr)> cb) {
auto weak = GetWeakPtr();
(void)heap_dispatcher_.Post(
[self = std::move(weak), cb = std::move(cb)](pw::async::Context /*ctx*/,
pw::Status status) {
if (status.ok()) {
cb(self);
}
});
}
uint8_t value() const { return value_; }
private:
uint8_t value_;
pw::async::HeapDispatcher heap_dispatcher_;
};
TEST_F(WeakSelfTest, InvalidatingSelf) {
bool called = false;
FunctionTester::WeakPtr ptr;
// Default-constructed weak pointers are not alive.
EXPECT_FALSE(ptr.is_alive());
auto cb = [&ptr, &called](auto weakptr) {
called = true;
ptr = weakptr;
};
{
FunctionTester test(0xBA, dispatcher());
test.callback_later_with_weak(cb);
// Run the loop until we're called back.
RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_TRUE(ptr.is_alive());
EXPECT_EQ(&test, &ptr.get());
EXPECT_EQ(0xBA, ptr->value());
called = false;
test.callback_later_with_weak(cb);
// Now out of scope.
}
// Run the loop until we're called back.
RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_FALSE(ptr.is_alive());
EXPECT_DEATH_IF_SUPPORTED(ptr.get(), "destroyed");
}
TEST_F(WeakSelfTest, InvalidatePtrs) {
bool called = false;
FunctionTester::WeakPtr ptr;
// Default-constructed weak pointers are not alive.
EXPECT_FALSE(ptr.is_alive());
auto cb = [&ptr, &called](auto weakptr) {
called = true;
ptr = weakptr;
};
FunctionTester test(0xBA, dispatcher());
test.callback_later_with_weak(cb);
// Run the loop until we're called back.
RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_TRUE(ptr.is_alive());
EXPECT_EQ(&test, &ptr.get());
EXPECT_EQ(0xBA, ptr->value());
called = false;
test.callback_later_with_weak(cb);
// Now invalidate the pointers.
test.InvalidatePtrs();
// Run the loop until we're called back.
RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_FALSE(ptr.is_alive());
EXPECT_DEATH_IF_SUPPORTED(ptr.get(), "destroyed");
}
class StaticTester;
class OnlyTwoStaticManager {
public:
explicit OnlyTwoStaticManager(StaticTester* self_ptr) : obj_ptr_(self_ptr) {}
~OnlyTwoStaticManager() { InvalidateAll(); }
using RefType = RecyclingWeakRef;
std::optional<pw::IntrusivePtr<RefType>> GetWeakRef() {
for (auto& ptr : OnlyTwoStaticManager::pointers_) {
if (ptr.is_alive() && ptr.get() == obj_ptr_) {
// Already adopted, add another refptr pointing to it.
return pw::IntrusivePtr(&ptr);
}
}
for (auto& ptr : OnlyTwoStaticManager::pointers_) {
if (!ptr.is_in_use()) {
return ptr.alloc(obj_ptr_);
}
}
return std::nullopt;
}
void InvalidateAll() {
OnlyTwoStaticManager::pointers_[0].maybe_unset(obj_ptr_);
OnlyTwoStaticManager::pointers_[1].maybe_unset(obj_ptr_);
}
private:
StaticTester* obj_ptr_;
inline static RecyclingWeakRef pointers_[2];
};
class StaticTester : public WeakSelf<StaticTester, OnlyTwoStaticManager> {
public:
explicit StaticTester(uint8_t testval) : WeakSelf(this), value_(testval) {}
uint8_t value() const { return value_; }
private:
uint8_t value_;
};
TEST_F(WeakSelfTest, StaticRecyclingPointers) {
// We can create more objects than we have weak space for.
StaticTester test1(1);
StaticTester test2(2);
StaticTester test3(3);
// And create as many weak pointers of one of them as we want.
auto ptr = test1.GetWeakPtr();
auto ptr2 = test1.GetWeakPtr();
auto ptr3 = test1.GetWeakPtr();
auto ptr4 = ptr;
// Make the second one have some ptrs too.
{
{
StaticTester test4(4);
auto second_ptr = test4.GetWeakPtr();
auto second_ptr2 = test4.GetWeakPtr();
EXPECT_TRUE(ptr4.is_alive());
StaticTester* ptr4_old = &ptr4.get();
ptr4 = second_ptr;
EXPECT_TRUE(ptr4.is_alive());
// It's moved to the new one though.
EXPECT_NE(&ptr4.get(), ptr4_old);
EXPECT_EQ(&ptr4.get(), &test4);
}
// ptr4 outlived it's target.
EXPECT_FALSE(ptr4.is_alive());
// Now let's make the second weak pointer unused, recycling it.
ptr4 = ptr3;
}
// Now I can get a second weak ptr still, from our third object.
auto still_okay = test3.GetWeakPtr();
auto still_copy = still_okay;
EXPECT_TRUE(still_copy.is_alive());
}
TEST_F(WeakSelfTest, StaticDeathWhenExhausted) {
StaticTester test1(1);
StaticTester test3(3);
auto ptr1 = test1.GetWeakPtr();
auto ptr2 = ptr1;
{
StaticTester test2(2);
ptr2 = test2.GetWeakPtr();
EXPECT_TRUE(ptr2.is_alive());
EXPECT_TRUE(ptr1.is_alive());
}
EXPECT_FALSE(ptr2.is_alive());
EXPECT_DEATH_IF_SUPPORTED(test3.GetWeakPtr(), ".*");
}
class BaseClass {
public:
BaseClass() = default;
virtual ~BaseClass() = default;
void set_value(int value) { value_ = value; }
int value() const { return value_; }
private:
int value_ = 0;
};
class ChildClass : public BaseClass, public WeakSelf<ChildClass> {
public:
ChildClass() : BaseClass(), WeakSelf<ChildClass>(this) {}
};
TEST_F(WeakSelfTest, Upcast) {
ChildClass obj;
WeakPtr<ChildClass> child_weak = obj.GetWeakPtr();
child_weak->set_value(1);
EXPECT_EQ(child_weak->value(), 1);
WeakPtr<BaseClass> base_weak_copy(child_weak);
EXPECT_TRUE(child_weak.is_alive());
base_weak_copy->set_value(2);
EXPECT_EQ(base_weak_copy->value(), 2);
WeakPtr<BaseClass> base_weak_move(std::move(child_weak));
EXPECT_FALSE(child_weak.is_alive());
base_weak_move->set_value(3);
EXPECT_EQ(base_weak_move->value(), 3);
}
} // namespace
} // namespace bt