|  | // Copyright 2018 The Chromium 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 "base/task_scheduler/tracked_ref.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/macros.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 { | 
|  | namespace internal { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class ObjectWithTrackedRefs { | 
|  | public: | 
|  | ObjectWithTrackedRefs() : tracked_ref_factory_(this) {} | 
|  | ~ObjectWithTrackedRefs() { under_destruction_.Set(); } | 
|  |  | 
|  | TrackedRef<ObjectWithTrackedRefs> GetTrackedRef() { | 
|  | return tracked_ref_factory_.GetTrackedRef(); | 
|  | } | 
|  |  | 
|  | bool under_destruction() const { return under_destruction_.IsSet(); } | 
|  |  | 
|  | private: | 
|  | // True once ~ObjectWithTrackedRefs() has been initiated. | 
|  | AtomicFlag under_destruction_; | 
|  |  | 
|  | TrackedRefFactory<ObjectWithTrackedRefs> tracked_ref_factory_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ObjectWithTrackedRefs); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Test that an object with a TrackedRefFactory can be destroyed by a single | 
|  | // owner but that its destruction will be blocked on the TrackedRefs being | 
|  | // released. | 
|  | 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) { | 
|  | // By the time this kicks in, the object should already be under | 
|  | // destruction, but blocked on this TrackedRef being released. This | 
|  | // is technically racy (main thread has to run |obj.reset()| and | 
|  | // this thread has to observe the side-effects before this delayed | 
|  | // task fires). If this ever flakes this expectation could be turned | 
|  | // into a while(!obj->under_destruction()); but until that's proven | 
|  | // flaky in practice, this expectation is more readable and | 
|  | // diagnosable then a hang. | 
|  | EXPECT_TRUE(obj->under_destruction()); | 
|  | }, | 
|  | obj->GetTrackedRef()), | 
|  | TestTimeouts::tiny_timeout()); | 
|  |  | 
|  | // This should kick off destruction but block until the above task resolves | 
|  | // and releases the TrackedRef. | 
|  | 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>(); | 
|  |  | 
|  | // Send a TrackedRef to each thread. | 
|  | for (auto& thread : threads) { | 
|  | thread->task_runner()->PostTask( | 
|  | FROM_HERE, BindOnce( | 
|  | [](TrackedRef<ObjectWithTrackedRefs> obj) { | 
|  | // Confirm it's still safe to | 
|  | // dereference |obj| (and, bonus, that | 
|  | // playing with TrackedRefs some more | 
|  | // isn't problematic). | 
|  | EXPECT_TRUE(obj->GetTrackedRef()); | 
|  | }, | 
|  | obj->GetTrackedRef())); | 
|  | } | 
|  |  | 
|  | // Initiate destruction racily with the above tasks' execution (they will | 
|  | // crash if TrackedRefs aren't WAI). | 
|  | obj.reset(); | 
|  | } | 
|  |  | 
|  | // Test that instantiating and deleting a TrackedRefFactory without ever taking | 
|  | // a TrackedRef on it is fine. | 
|  | TEST(TrackedRefTest, NoTrackedRefs) { | 
|  | ObjectWithTrackedRefs obj; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | void ConsumesTrackedRef(TrackedRef<ObjectWithTrackedRefs> obj) {} | 
|  | }  // namespace | 
|  |  | 
|  | // Test that destroying a TrackedRefFactory which had TrackedRefs in the past | 
|  | // that are already gone is WAI. | 
|  | 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()); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace base |