blob: 43beb63947e1fa8ea70b9858e805c4f2f4f13c49 [file] [log] [blame]
// 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