| /* | |
| * 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 |