|  | /* | 
|  | *  Copyright 2019 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "api/task_queue/pending_task_safety_flag.h" | 
|  |  | 
|  | #include <memory> | 
|  | #include <utility> | 
|  |  | 
|  | #include "api/scoped_refptr.h" | 
|  | #include "api/task_queue/task_queue_base.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/event.h" | 
|  | #include "rtc_base/task_queue_for_test.h" | 
|  | #include "test/gtest.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | TEST(PendingTaskSafetyFlagTest, Basic) { | 
|  | scoped_refptr<PendingTaskSafetyFlag> safety_flag; | 
|  | { | 
|  | // Scope for the `owner` instance. | 
|  | class Owner { | 
|  | public: | 
|  | Owner() = default; | 
|  | ~Owner() { flag_->SetNotAlive(); } | 
|  |  | 
|  | scoped_refptr<PendingTaskSafetyFlag> flag_ = | 
|  | PendingTaskSafetyFlag::Create(); | 
|  | } owner; | 
|  | EXPECT_TRUE(owner.flag_->alive()); | 
|  | safety_flag = owner.flag_; | 
|  | EXPECT_TRUE(safety_flag->alive()); | 
|  | } | 
|  | // `owner` now out of scope. | 
|  | EXPECT_FALSE(safety_flag->alive()); | 
|  | } | 
|  |  | 
|  | TEST(PendingTaskSafetyFlagTest, BasicScoped) { | 
|  | scoped_refptr<PendingTaskSafetyFlag> safety_flag; | 
|  | { | 
|  | struct Owner { | 
|  | ScopedTaskSafety safety; | 
|  | } owner; | 
|  | safety_flag = owner.safety.flag(); | 
|  | EXPECT_TRUE(safety_flag->alive()); | 
|  | } | 
|  | // `owner` now out of scope. | 
|  | EXPECT_FALSE(safety_flag->alive()); | 
|  | } | 
|  |  | 
|  | TEST(PendingTaskSafetyFlagTest, PendingTaskSuccess) { | 
|  | TaskQueueForTest tq1("OwnerHere"); | 
|  | TaskQueueForTest tq2("OwnerNotHere"); | 
|  |  | 
|  | class Owner { | 
|  | public: | 
|  | Owner() : tq_main_(TaskQueueBase::Current()) { RTC_DCHECK(tq_main_); } | 
|  | ~Owner() { | 
|  | RTC_DCHECK(tq_main_->IsCurrent()); | 
|  | flag_->SetNotAlive(); | 
|  | } | 
|  |  | 
|  | void DoStuff() { | 
|  | RTC_DCHECK(!tq_main_->IsCurrent()); | 
|  | scoped_refptr<PendingTaskSafetyFlag> safe = flag_; | 
|  | tq_main_->PostTask([safe = std::move(safe), this]() { | 
|  | if (!safe->alive()) | 
|  | return; | 
|  | stuff_done_ = true; | 
|  | }); | 
|  | } | 
|  |  | 
|  | bool stuff_done() const { return stuff_done_; } | 
|  |  | 
|  | private: | 
|  | TaskQueueBase* const tq_main_; | 
|  | bool stuff_done_ = false; | 
|  | scoped_refptr<PendingTaskSafetyFlag> flag_ = | 
|  | PendingTaskSafetyFlag::Create(); | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<Owner> owner; | 
|  | tq1.SendTask([&owner]() { | 
|  | owner = std::make_unique<Owner>(); | 
|  | EXPECT_FALSE(owner->stuff_done()); | 
|  | }); | 
|  | ASSERT_TRUE(owner); | 
|  | tq2.SendTask([&owner]() { owner->DoStuff(); }); | 
|  | tq1.SendTask([&owner]() { | 
|  | EXPECT_TRUE(owner->stuff_done()); | 
|  | owner.reset(); | 
|  | }); | 
|  | ASSERT_FALSE(owner); | 
|  | } | 
|  |  | 
|  | TEST(PendingTaskSafetyFlagTest, PendingTaskDropped) { | 
|  | TaskQueueForTest tq1("OwnerHere"); | 
|  | TaskQueueForTest tq2("OwnerNotHere"); | 
|  |  | 
|  | class Owner { | 
|  | public: | 
|  | explicit Owner(bool* stuff_done) | 
|  | : tq_main_(TaskQueueBase::Current()), stuff_done_(stuff_done) { | 
|  | RTC_DCHECK(tq_main_); | 
|  | *stuff_done_ = false; | 
|  | } | 
|  | ~Owner() { RTC_DCHECK(tq_main_->IsCurrent()); } | 
|  |  | 
|  | void DoStuff() { | 
|  | RTC_DCHECK(!tq_main_->IsCurrent()); | 
|  | tq_main_->PostTask( | 
|  | SafeTask(safety_.flag(), [this]() { *stuff_done_ = true; })); | 
|  | } | 
|  |  | 
|  | private: | 
|  | TaskQueueBase* const tq_main_; | 
|  | bool* const stuff_done_; | 
|  | ScopedTaskSafety safety_; | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<Owner> owner; | 
|  | bool stuff_done = false; | 
|  | tq1.SendTask([&owner, &stuff_done]() { | 
|  | owner = std::make_unique<Owner>(&stuff_done); | 
|  | }); | 
|  | ASSERT_TRUE(owner); | 
|  | // Queue up a task on tq1 that will execute before the 'DoStuff' task | 
|  | // can, and delete the `owner` before the 'stuff' task can execute. | 
|  | Event blocker; | 
|  | tq1.PostTask([&blocker, &owner]() { | 
|  | blocker.Wait(Event::kForever); | 
|  | owner.reset(); | 
|  | }); | 
|  |  | 
|  | // Queue up a DoStuff... | 
|  | tq2.SendTask([&owner]() { owner->DoStuff(); }); | 
|  |  | 
|  | ASSERT_TRUE(owner); | 
|  | blocker.Set(); | 
|  |  | 
|  | // Run an empty task on tq1 to flush all the queued tasks. | 
|  | tq1.WaitForPreviouslyPostedTasks(); | 
|  | ASSERT_FALSE(owner); | 
|  | EXPECT_FALSE(stuff_done); | 
|  | } | 
|  |  | 
|  | TEST(PendingTaskSafetyFlagTest, PendingTaskNotAliveInitialized) { | 
|  | TaskQueueForTest tq("PendingTaskNotAliveInitialized"); | 
|  |  | 
|  | // Create a new flag that initially not `alive`. | 
|  | auto flag = PendingTaskSafetyFlag::CreateDetachedInactive(); | 
|  | tq.SendTask([&flag]() { EXPECT_FALSE(flag->alive()); }); | 
|  |  | 
|  | bool task_1_ran = false; | 
|  | bool task_2_ran = false; | 
|  | tq.PostTask(SafeTask(flag, [&task_1_ran]() { task_1_ran = true; })); | 
|  | tq.PostTask([&flag]() { flag->SetAlive(); }); | 
|  | tq.PostTask(SafeTask(flag, [&task_2_ran]() { task_2_ran = true; })); | 
|  |  | 
|  | tq.WaitForPreviouslyPostedTasks(); | 
|  | EXPECT_FALSE(task_1_ran); | 
|  | EXPECT_TRUE(task_2_ran); | 
|  | } | 
|  |  | 
|  | TEST(PendingTaskSafetyFlagTest, PendingTaskInitializedForTaskQueue) { | 
|  | TaskQueueForTest tq("PendingTaskAliveInitializedForTaskQueue"); | 
|  |  | 
|  | // Create a new flag that initially `alive`, attached to a specific TQ. | 
|  | auto flag = PendingTaskSafetyFlag::CreateAttachedToTaskQueue(true, tq.Get()); | 
|  | tq.SendTask([&flag]() { EXPECT_TRUE(flag->alive()); }); | 
|  | // Repeat the same steps but initialize as inactive. | 
|  | flag = PendingTaskSafetyFlag::CreateAttachedToTaskQueue(false, tq.Get()); | 
|  | tq.SendTask([&flag]() { EXPECT_FALSE(flag->alive()); }); | 
|  | } | 
|  |  | 
|  | TEST(PendingTaskSafetyFlagTest, SafeTask) { | 
|  | scoped_refptr<PendingTaskSafetyFlag> flag = PendingTaskSafetyFlag::Create(); | 
|  |  | 
|  | int count = 0; | 
|  | // Create two identical tasks that increment the `count`. | 
|  | auto task1 = SafeTask(flag, [&count] { ++count; }); | 
|  | auto task2 = SafeTask(flag, [&count] { ++count; }); | 
|  |  | 
|  | EXPECT_EQ(count, 0); | 
|  | std::move(task1)(); | 
|  | EXPECT_EQ(count, 1); | 
|  | flag->SetNotAlive(); | 
|  | // Now task2 should actually not run. | 
|  | std::move(task2)(); | 
|  | EXPECT_EQ(count, 1); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |