#include "base/task/thread_pool/tracked_ref.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/synchronization/atomic_flag.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base::internal {
namespace {
class ObjectWithTrackedRefs {
public:
ObjectWithTrackedRefs() : tracked_ref_factory_(this) {}
ObjectWithTrackedRefs(const ObjectWithTrackedRefs&) = delete;
ObjectWithTrackedRefs& operator=(const ObjectWithTrackedRefs&) = delete;
~ObjectWithTrackedRefs() { under_destruction_.Set(); }
TrackedRef<ObjectWithTrackedRefs> GetTrackedRef() {
return tracked_ref_factory_.GetTrackedRef();
}
bool under_destruction() const { return under_destruction_.IsSet(); }
private:
AtomicFlag under_destruction_;
TrackedRefFactory<ObjectWithTrackedRefs> tracked_ref_factory_;
};
}
TEST(TrackedRefTest, TrackedRefObjectDeletion) {
Thread thread("TrackedRefTestThread");
thread.Start();
std::unique_ptr<ObjectWithTrackedRefs> obj =
std::make_unique<ObjectWithTrackedRefs>();
TimeTicks begin = TimeTicks::Now();
thread.task_runner()->PostDelayedTask(
FROM_HERE,
BindOnce(
[](TrackedRef<ObjectWithTrackedRefs> obj) {
EXPECT_TRUE(obj->under_destruction());
},
obj->GetTrackedRef()),
TestTimeouts::tiny_timeout());
obj.reset();
EXPECT_GE(TimeTicks::Now() - begin, TestTimeouts::tiny_timeout());
}
TEST(TrackedRefTest, ManyThreadsRacing) {
constexpr int kNumThreads = 16;
std::vector<std::unique_ptr<Thread>> threads;
for (int i = 0; i < kNumThreads; ++i) {
threads.push_back(std::make_unique<Thread>("TrackedRefTestThread"));
threads.back()->StartAndWaitForTesting();
}
std::unique_ptr<ObjectWithTrackedRefs> obj =
std::make_unique<ObjectWithTrackedRefs>();
for (auto& thread : threads) {
thread->task_runner()->PostTask(
FROM_HERE, BindOnce(
[](TrackedRef<ObjectWithTrackedRefs> obj) {
EXPECT_TRUE(obj->GetTrackedRef());
},
obj->GetTrackedRef()));
}
obj.reset();
}
TEST(TrackedRefTest, NoTrackedRefs) {
ObjectWithTrackedRefs obj;
}
namespace {
void ConsumesTrackedRef(TrackedRef<ObjectWithTrackedRefs> obj) {}
}
TEST(TrackedRefTest, NoPendingTrackedRefs) {
ObjectWithTrackedRefs obj;
ConsumesTrackedRef(obj.GetTrackedRef());
}
TEST(TrackedRefTest, CopyAndMoveSemantics) {
struct Foo {
Foo() : factory(this) {}
TrackedRefFactory<Foo> factory;
};
Foo foo;
EXPECT_EQ(1, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
{
TrackedRef<Foo> plain = foo.factory.GetTrackedRef();
EXPECT_EQ(2, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
TrackedRef<Foo> copy_constructed(plain);
EXPECT_EQ(3, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
TrackedRef<Foo> moved_constructed(std::move(copy_constructed));
EXPECT_EQ(3, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
}
EXPECT_EQ(1, foo.factory.live_tracked_refs_.SubtleRefCountForDebug());
}
}