| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #define FML_USED_ON_EMBEDDER |
| |
| #include "flutter/fml/raster_thread_merger.h" |
| |
| #include <thread> |
| |
| #include "flutter/fml/memory/ref_ptr.h" |
| #include "flutter/fml/message_loop.h" |
| #include "flutter/fml/synchronization/count_down_latch.h" |
| #include "flutter/fml/synchronization/waitable_event.h" |
| #include "flutter/fml/task_runner.h" |
| #include "flutter/fml/thread.h" |
| #include "gtest/gtest.h" |
| |
| namespace fml { |
| namespace testing { |
| |
| /// A mock task queue NOT calling MessageLoop->Run() in thread |
| struct TaskQueueWrapper { |
| fml::MessageLoop* loop = nullptr; |
| |
| /// The waiter for message loop initialized ok |
| fml::AutoResetWaitableEvent latch; |
| |
| /// The waiter for thread finished |
| fml::AutoResetWaitableEvent term; |
| |
| /// This field must below latch and term member, because |
| /// cpp standard reference: |
| /// non-static data members are initialized in the order they were declared in |
| /// the class definition |
| std::thread thread; |
| |
| TaskQueueWrapper() |
| : thread([this]() { |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| loop = &fml::MessageLoop::GetCurrent(); |
| latch.Signal(); |
| term.Wait(); |
| }) { |
| latch.Wait(); |
| } |
| |
| ~TaskQueueWrapper() { |
| term.Signal(); |
| thread.join(); |
| } |
| |
| fml::TaskQueueId GetTaskQueueId() const { |
| return loop->GetTaskRunner()->GetTaskQueueId(); |
| } |
| }; |
| |
| TEST(RasterThreadMerger, RemainMergedTillLeaseExpires) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| const size_t kNumFramesMerged = 5; |
| |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->MergeWithLease(kNumFramesMerged); |
| |
| for (size_t i = 0; i < kNumFramesMerged; i++) { |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| raster_thread_merger->DecrementLease(); |
| } |
| |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| } |
| |
| TEST(RasterThreadMerger, IsNotOnRasterizingThread) { |
| fml::MessageLoop* loop1 = nullptr; |
| fml::AutoResetWaitableEvent latch1; |
| std::thread thread1([&loop1, &latch1]() { |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| loop1 = &fml::MessageLoop::GetCurrent(); |
| loop1->GetTaskRunner()->PostTask([&]() { latch1.Signal(); }); |
| loop1->Run(); |
| }); |
| |
| fml::MessageLoop* loop2 = nullptr; |
| fml::AutoResetWaitableEvent latch2; |
| std::thread thread2([&loop2, &latch2]() { |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| loop2 = &fml::MessageLoop::GetCurrent(); |
| loop2->GetTaskRunner()->PostTask([&]() { latch2.Signal(); }); |
| loop2->Run(); |
| }); |
| |
| latch1.Wait(); |
| latch2.Wait(); |
| |
| fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); |
| fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| |
| fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2); |
| |
| loop1->GetTaskRunner()->PostTask([&]() { |
| ASSERT_FALSE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); |
| pre_merge.CountDown(); |
| }); |
| |
| loop2->GetTaskRunner()->PostTask([&]() { |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2); |
| pre_merge.CountDown(); |
| }); |
| |
| pre_merge.Wait(); |
| |
| raster_thread_merger->MergeWithLease(1); |
| |
| loop1->GetTaskRunner()->PostTask([&]() { |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); |
| post_merge.CountDown(); |
| }); |
| |
| loop2->GetTaskRunner()->PostTask([&]() { |
| // this will be false since this is going to be run |
| // on loop1 really. |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); |
| post_merge.CountDown(); |
| }); |
| |
| post_merge.Wait(); |
| |
| raster_thread_merger->DecrementLease(); |
| |
| loop1->GetTaskRunner()->PostTask([&]() { |
| ASSERT_FALSE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); |
| post_unmerge.CountDown(); |
| }); |
| |
| loop2->GetTaskRunner()->PostTask([&]() { |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2); |
| post_unmerge.CountDown(); |
| }); |
| |
| post_unmerge.Wait(); |
| |
| loop1->GetTaskRunner()->PostTask([&]() { loop1->Terminate(); }); |
| |
| loop2->GetTaskRunner()->PostTask([&]() { loop2->Terminate(); }); |
| |
| thread1.join(); |
| thread2.join(); |
| } |
| |
| TEST(RasterThreadMerger, LeaseExtension) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| const size_t kNumFramesMerged = 5; |
| |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->MergeWithLease(kNumFramesMerged); |
| |
| // let there be one more turn till the leases expire. |
| for (size_t i = 0; i < kNumFramesMerged - 1; i++) { |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| raster_thread_merger->DecrementLease(); |
| } |
| |
| // extend the lease once. |
| raster_thread_merger->ExtendLeaseTo(kNumFramesMerged); |
| |
| // we will NOT last for 1 extra turn, we just set it. |
| for (size_t i = 0; i < kNumFramesMerged; i++) { |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| raster_thread_merger->DecrementLease(); |
| } |
| |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| } |
| |
| TEST(RasterThreadMerger, WaitUntilMerged) { |
| fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger; |
| |
| fml::AutoResetWaitableEvent create_thread_merger_latch; |
| fml::MessageLoop* loop_platform = nullptr; |
| fml::AutoResetWaitableEvent latch_platform; |
| fml::AutoResetWaitableEvent term_platform; |
| fml::AutoResetWaitableEvent latch_merged; |
| std::thread thread_platform([&]() { |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| loop_platform = &fml::MessageLoop::GetCurrent(); |
| latch_platform.Signal(); |
| create_thread_merger_latch.Wait(); |
| raster_thread_merger->WaitUntilMerged(); |
| latch_merged.Signal(); |
| term_platform.Wait(); |
| }); |
| |
| const size_t kNumFramesMerged = 5; |
| fml::MessageLoop* loop_raster = nullptr; |
| fml::AutoResetWaitableEvent term_raster; |
| std::thread thread_raster([&]() { |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| loop_raster = &fml::MessageLoop::GetCurrent(); |
| latch_platform.Wait(); |
| fml::TaskQueueId qid_platform = |
| loop_platform->GetTaskRunner()->GetTaskQueueId(); |
| fml::TaskQueueId qid_raster = |
| loop_raster->GetTaskRunner()->GetTaskQueueId(); |
| raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid_platform, qid_raster); |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| create_thread_merger_latch.Signal(); |
| raster_thread_merger->MergeWithLease(kNumFramesMerged); |
| term_raster.Wait(); |
| }); |
| |
| latch_merged.Wait(); |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| for (size_t i = 0; i < kNumFramesMerged; i++) { |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| raster_thread_merger->DecrementLease(); |
| } |
| |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| term_platform.Signal(); |
| term_raster.Signal(); |
| thread_platform.join(); |
| thread_raster.join(); |
| } |
| |
| TEST(RasterThreadMerger, HandleTaskQueuesAreTheSame) { |
| TaskQueueWrapper queue; |
| fml::TaskQueueId qid1 = queue.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = qid1; |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| // Statically merged. |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| // Test decrement lease and unmerge are both no-ops. |
| // The task queues should be always merged. |
| const size_t kNumFramesMerged = 5; |
| raster_thread_merger->MergeWithLease(kNumFramesMerged); |
| |
| for (size_t i = 0; i < kNumFramesMerged; i++) { |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| raster_thread_merger->DecrementLease(); |
| } |
| |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| // Wait until merged should also return immediately. |
| raster_thread_merger->WaitUntilMerged(); |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| } |
| |
| TEST(RasterThreadMerger, Enable) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| |
| raster_thread_merger->Disable(); |
| raster_thread_merger->MergeWithLease(1); |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->Enable(); |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->MergeWithLease(1); |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->DecrementLease(); |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| } |
| |
| TEST(RasterThreadMerger, Disable) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| |
| raster_thread_merger->Disable(); |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->MergeWithLease(1); |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->Enable(); |
| raster_thread_merger->MergeWithLease(1); |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->Disable(); |
| raster_thread_merger->UnMergeNowIfLastOne(); |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| { |
| auto decrement_result = raster_thread_merger->DecrementLease(); |
| ASSERT_EQ(fml::RasterThreadStatus::kRemainsMerged, decrement_result); |
| } |
| |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->Enable(); |
| raster_thread_merger->UnMergeNowIfLastOne(); |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| |
| raster_thread_merger->MergeWithLease(1); |
| |
| ASSERT_TRUE(raster_thread_merger->IsMerged()); |
| |
| { |
| auto decrement_result = raster_thread_merger->DecrementLease(); |
| ASSERT_EQ(fml::RasterThreadStatus::kUnmergedNow, decrement_result); |
| } |
| |
| ASSERT_FALSE(raster_thread_merger->IsMerged()); |
| } |
| |
| TEST(RasterThreadMerger, IsEnabled) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| ASSERT_TRUE(raster_thread_merger->IsEnabled()); |
| |
| raster_thread_merger->Disable(); |
| ASSERT_FALSE(raster_thread_merger->IsEnabled()); |
| |
| raster_thread_merger->Enable(); |
| ASSERT_TRUE(raster_thread_merger->IsEnabled()); |
| } |
| |
| TEST(RasterThreadMerger, TwoMergersWithSameThreadPairShareEnabledState) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| const auto merger1 = |
| RasterThreadMerger::CreateOrShareThreadMerger(nullptr, qid1, qid2); |
| const auto merger2 = |
| RasterThreadMerger::CreateOrShareThreadMerger(merger1, qid1, qid2); |
| ASSERT_TRUE(merger1->IsEnabled()); |
| ASSERT_TRUE(merger2->IsEnabled()); |
| |
| merger1->Disable(); |
| ASSERT_FALSE(merger1->IsEnabled()); |
| ASSERT_FALSE(merger2->IsEnabled()); |
| |
| merger2->Enable(); |
| ASSERT_TRUE(merger1->IsEnabled()); |
| ASSERT_TRUE(merger2->IsEnabled()); |
| } |
| |
| TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskMergesThreads) { |
| fml::MessageLoop* loop_platform = nullptr; |
| fml::AutoResetWaitableEvent latch1; |
| std::thread thread_platform([&loop_platform, &latch1]() { |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| loop_platform = &fml::MessageLoop::GetCurrent(); |
| loop_platform->GetTaskRunner()->PostTask([&]() { latch1.Signal(); }); |
| loop_platform->Run(); |
| }); |
| |
| fml::MessageLoop* loop_raster = nullptr; |
| fml::AutoResetWaitableEvent latch2; |
| std::thread thread_raster([&loop_raster, &loop_platform, &latch1, &latch2]() { |
| latch1.Wait(); |
| |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| loop_raster = &fml::MessageLoop::GetCurrent(); |
| fml::TaskQueueId qid_platform = |
| loop_platform->GetTaskRunner()->GetTaskQueueId(); |
| fml::TaskQueueId qid_raster = |
| loop_raster->GetTaskRunner()->GetTaskQueueId(); |
| fml::CountDownLatch post_merge(2); |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid_platform, qid_raster); |
| loop_raster->GetTaskRunner()->PostTask([&]() { |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_raster); |
| raster_thread_merger->MergeWithLease(1); |
| post_merge.CountDown(); |
| }); |
| |
| loop_raster->GetTaskRunner()->PostTask([&]() { |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform); |
| raster_thread_merger->DecrementLease(); |
| post_merge.CountDown(); |
| }); |
| |
| loop_raster->RunExpiredTasksNow(); |
| post_merge.Wait(); |
| latch2.Signal(); |
| }); |
| |
| latch2.Wait(); |
| loop_platform->GetTaskRunner()->PostTask( |
| [&]() { loop_platform->Terminate(); }); |
| |
| thread_platform.join(); |
| thread_raster.join(); |
| } |
| |
| TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskUnMergesThreads) { |
| fml::Thread platform_thread("test_platform_thread"); |
| |
| fml::AutoResetWaitableEvent raster_latch; |
| std::thread thread_raster([&]() { |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| fml::MessageLoop* loop_raster = &fml::MessageLoop::GetCurrent(); |
| |
| fml::TaskQueueId qid_platform = |
| platform_thread.GetTaskRunner()->GetTaskQueueId(); |
| fml::TaskQueueId qid_raster = |
| loop_raster->GetTaskRunner()->GetTaskQueueId(); |
| |
| fml::AutoResetWaitableEvent merge_latch; |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid_platform, qid_raster); |
| loop_raster->GetTaskRunner()->PostTask([&]() { |
| raster_thread_merger->MergeWithLease(1); |
| merge_latch.Signal(); |
| }); |
| |
| loop_raster->RunExpiredTasksNow(); |
| merge_latch.Wait(); |
| |
| // threads should be merged at this point. |
| fml::AutoResetWaitableEvent unmerge_latch; |
| loop_raster->GetTaskRunner()->PostTask([&]() { |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_TRUE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform); |
| raster_thread_merger->DecrementLease(); |
| unmerge_latch.Signal(); |
| }); |
| |
| fml::AutoResetWaitableEvent post_unmerge_latch; |
| loop_raster->GetTaskRunner()->PostTask([&]() { |
| ASSERT_TRUE(raster_thread_merger->IsOnRasterizingThread()); |
| ASSERT_FALSE(raster_thread_merger->IsOnPlatformThread()); |
| ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_raster); |
| post_unmerge_latch.Signal(); |
| }); |
| |
| unmerge_latch.Wait(); |
| loop_raster->RunExpiredTasksNow(); |
| |
| post_unmerge_latch.Wait(); |
| raster_latch.Signal(); |
| }); |
| |
| raster_latch.Wait(); |
| thread_raster.join(); |
| } |
| |
| TEST(RasterThreadMerger, SetMergeUnmergeCallback) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| |
| const auto raster_thread_merger = |
| fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2); |
| |
| int callbacks = 0; |
| raster_thread_merger->SetMergeUnmergeCallback( |
| [&callbacks]() { callbacks++; }); |
| |
| ASSERT_EQ(0, callbacks); |
| |
| raster_thread_merger->MergeWithLease(1); |
| ASSERT_EQ(1, callbacks); |
| |
| raster_thread_merger->DecrementLease(); |
| ASSERT_EQ(2, callbacks); |
| } |
| |
| TEST(RasterThreadMerger, MultipleMergersCanMergeSameThreadPair) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| // Two mergers will share one same inner merger |
| const auto raster_thread_merger1 = |
| fml::RasterThreadMerger::CreateOrShareThreadMerger(nullptr, qid1, qid2); |
| const auto raster_thread_merger2 = |
| fml::RasterThreadMerger::CreateOrShareThreadMerger(raster_thread_merger1, |
| qid1, qid2); |
| const size_t kNumFramesMerged = 5; |
| ASSERT_FALSE(raster_thread_merger1->IsMerged()); |
| ASSERT_FALSE(raster_thread_merger2->IsMerged()); |
| |
| // Merge using the first merger |
| raster_thread_merger1->MergeWithLease(kNumFramesMerged); |
| |
| ASSERT_TRUE(raster_thread_merger1->IsMerged()); |
| ASSERT_TRUE(raster_thread_merger2->IsMerged()); |
| |
| // let there be one more turn till the leases expire. |
| for (size_t i = 0; i < kNumFramesMerged - 1; i++) { |
| // Check merge state using the two merger |
| ASSERT_TRUE(raster_thread_merger1->IsMerged()); |
| ASSERT_TRUE(raster_thread_merger2->IsMerged()); |
| raster_thread_merger1->DecrementLease(); |
| } |
| |
| ASSERT_TRUE(raster_thread_merger1->IsMerged()); |
| ASSERT_TRUE(raster_thread_merger2->IsMerged()); |
| |
| // extend the lease once with the first merger |
| raster_thread_merger1->ExtendLeaseTo(kNumFramesMerged); |
| |
| // we will NOT last for 1 extra turn, we just set it. |
| for (size_t i = 0; i < kNumFramesMerged; i++) { |
| // Check merge state using the two merger |
| ASSERT_TRUE(raster_thread_merger1->IsMerged()); |
| ASSERT_TRUE(raster_thread_merger2->IsMerged()); |
| raster_thread_merger1->DecrementLease(); |
| } |
| |
| ASSERT_FALSE(raster_thread_merger1->IsMerged()); |
| ASSERT_FALSE(raster_thread_merger2->IsMerged()); |
| } |
| |
| TEST(RasterThreadMerger, TheLastCallerOfMultipleMergersCanUnmergeNow) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| // Two mergers will share one same inner merger |
| const auto raster_thread_merger1 = |
| fml::RasterThreadMerger::CreateOrShareThreadMerger(nullptr, qid1, qid2); |
| const auto raster_thread_merger2 = |
| fml::RasterThreadMerger::CreateOrShareThreadMerger(raster_thread_merger1, |
| qid1, qid2); |
| const size_t kNumFramesMerged = 5; |
| ASSERT_FALSE(raster_thread_merger1->IsMerged()); |
| ASSERT_FALSE(raster_thread_merger2->IsMerged()); |
| |
| // Merge using the mergers |
| raster_thread_merger1->MergeWithLease(kNumFramesMerged); |
| ASSERT_TRUE(raster_thread_merger1->IsMerged()); |
| ASSERT_TRUE(raster_thread_merger2->IsMerged()); |
| // Extend the second merger's lease |
| raster_thread_merger2->ExtendLeaseTo(kNumFramesMerged); |
| ASSERT_TRUE(raster_thread_merger1->IsMerged()); |
| ASSERT_TRUE(raster_thread_merger2->IsMerged()); |
| |
| // Two callers state becomes one caller left. |
| raster_thread_merger1->UnMergeNowIfLastOne(); |
| // Check if still merged |
| ASSERT_TRUE(raster_thread_merger1->IsMerged()); |
| ASSERT_TRUE(raster_thread_merger2->IsMerged()); |
| |
| // One caller state becomes no callers left. |
| raster_thread_merger2->UnMergeNowIfLastOne(); |
| // Check if unmerged |
| ASSERT_FALSE(raster_thread_merger1->IsMerged()); |
| ASSERT_FALSE(raster_thread_merger2->IsMerged()); |
| } |
| |
| /// This case tests multiple standalone engines using independent merger to |
| /// merge two different raster threads into the same platform thread. |
| TEST(RasterThreadMerger, |
| TwoIndependentMergersCanMergeTwoDifferentThreadsIntoSamePlatformThread) { |
| TaskQueueWrapper queue1; |
| TaskQueueWrapper queue2; |
| TaskQueueWrapper queue3; |
| fml::TaskQueueId qid1 = queue1.GetTaskQueueId(); |
| fml::TaskQueueId qid2 = queue2.GetTaskQueueId(); |
| fml::TaskQueueId qid3 = queue3.GetTaskQueueId(); |
| |
| // Two mergers will NOT share same inner merger |
| const auto merger_from_2_to_1 = |
| fml::RasterThreadMerger::CreateOrShareThreadMerger(nullptr, qid1, qid2); |
| const auto merger_from_3_to_1 = |
| fml::RasterThreadMerger::CreateOrShareThreadMerger(merger_from_2_to_1, |
| qid1, qid3); |
| const size_t kNumFramesMerged = 5; |
| ASSERT_FALSE(merger_from_2_to_1->IsMerged()); |
| ASSERT_FALSE(merger_from_3_to_1->IsMerged()); |
| |
| // Merge thread2 into thread1 |
| merger_from_2_to_1->MergeWithLease(kNumFramesMerged); |
| // Merge thread3 into thread1 |
| merger_from_3_to_1->MergeWithLease(kNumFramesMerged); |
| |
| ASSERT_TRUE(merger_from_2_to_1->IsMerged()); |
| ASSERT_TRUE(merger_from_3_to_1->IsMerged()); |
| |
| for (size_t i = 0; i < kNumFramesMerged; i++) { |
| ASSERT_TRUE(merger_from_2_to_1->IsMerged()); |
| merger_from_2_to_1->DecrementLease(); |
| } |
| |
| ASSERT_FALSE(merger_from_2_to_1->IsMerged()); |
| ASSERT_TRUE(merger_from_3_to_1->IsMerged()); |
| |
| for (size_t i = 0; i < kNumFramesMerged; i++) { |
| ASSERT_TRUE(merger_from_3_to_1->IsMerged()); |
| merger_from_3_to_1->DecrementLease(); |
| } |
| |
| ASSERT_FALSE(merger_from_2_to_1->IsMerged()); |
| ASSERT_FALSE(merger_from_3_to_1->IsMerged()); |
| |
| merger_from_2_to_1->MergeWithLease(kNumFramesMerged); |
| ASSERT_TRUE(merger_from_2_to_1->IsMerged()); |
| ASSERT_FALSE(merger_from_3_to_1->IsMerged()); |
| merger_from_3_to_1->MergeWithLease(kNumFramesMerged); |
| ASSERT_TRUE(merger_from_2_to_1->IsMerged()); |
| ASSERT_TRUE(merger_from_3_to_1->IsMerged()); |
| |
| // Can unmerge independently |
| merger_from_2_to_1->UnMergeNowIfLastOne(); |
| ASSERT_FALSE(merger_from_2_to_1->IsMerged()); |
| ASSERT_TRUE(merger_from_3_to_1->IsMerged()); |
| |
| // Can unmerge independently |
| merger_from_3_to_1->UnMergeNowIfLastOne(); |
| ASSERT_FALSE(merger_from_2_to_1->IsMerged()); |
| ASSERT_FALSE(merger_from_3_to_1->IsMerged()); |
| } |
| |
| } // namespace testing |
| } // namespace fml |