Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fml/message_loop_task_queues.cc
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner) {
bool MessageLoopTaskQueues::Owns(TaskQueueId owner,
TaskQueueId subsumed) const {
std::lock_guard guard(queue_mutex_);
return subsumed == queue_entries_.at(owner)->owner_of || owner == subsumed;
return subsumed == queue_entries_.at(owner)->owner_of;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Would you mind adding a TODO, and filing an issue about reverting this logic once Android is fixed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually might not need to revert this as I think this make sense. waiting for @iskakaushik to confirm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, this is good.

}

// Subsumed queues will never have pending tasks.
Expand Down
7 changes: 7 additions & 0 deletions fml/message_loop_task_queues_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ TEST(MessageLoopTaskQueue, NotifyObserversWhileCreatingQueues) {
before_second_observer.Signal();
notify_observers.join();
}

TEST(MessageLoopTaskQueue, QueueDoNotOwnItself) {
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
auto queue_id = task_queue->CreateTaskQueue();
ASSERT_FALSE(task_queue->Owns(queue_id, queue_id));
}

// TODO(chunhtai): This unit-test is flaky and sometimes fails asynchronizely
// after the test has finished.
// https://github.com/flutter/flutter/issues/43858
Expand Down
23 changes: 22 additions & 1 deletion fml/raster_thread_merger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ RasterThreadMerger::RasterThreadMerger(fml::TaskQueueId platform_queue_id,
}

void RasterThreadMerger::MergeWithLease(size_t lease_term) {
if (TaskQueuesAreSame()) {
return;
}

FML_DCHECK(lease_term > 0) << "lease_term should be positive.";
std::scoped_lock lock(lease_term_mutex_);
if (!IsMergedUnSafe()) {
Expand All @@ -32,6 +36,10 @@ void RasterThreadMerger::MergeWithLease(size_t lease_term) {
}

void RasterThreadMerger::UnMergeNow() {
if (TaskQueuesAreSame()) {
return;
}

std::scoped_lock lock(lease_term_mutex_);
lease_term_ = 0;
bool success = task_queues_->Unmerge(platform_queue_id_);
Expand All @@ -51,6 +59,9 @@ bool RasterThreadMerger::IsOnRasterizingThread() {
}

void RasterThreadMerger::ExtendLeaseTo(size_t lease_term) {
if (TaskQueuesAreSame()) {
return;
}
std::scoped_lock lock(lease_term_mutex_);
FML_DCHECK(IsMergedUnSafe()) << "lease_term should be positive.";
if (lease_term_ != kLeaseNotSet &&
Expand All @@ -65,16 +76,26 @@ bool RasterThreadMerger::IsMerged() {
}

bool RasterThreadMerger::IsMergedUnSafe() {
return lease_term_ > 0;
return lease_term_ > 0 || TaskQueuesAreSame();
}

bool RasterThreadMerger::TaskQueuesAreSame() {
return platform_queue_id_ == gpu_queue_id_;
}

void RasterThreadMerger::WaitUntilMerged() {
if (TaskQueuesAreSame()) {
return;
}
FML_CHECK(IsOnPlatformThread());
std::unique_lock<std::mutex> lock(lease_term_mutex_);
merged_condition_.wait(lock, [&] { return IsMergedUnSafe(); });
}

RasterThreadStatus RasterThreadMerger::DecrementLease() {
if (TaskQueuesAreSame()) {
return RasterThreadStatus::kRemainsMerged;
}
std::unique_lock<std::mutex> lock(lease_term_mutex_);
if (!IsMergedUnSafe()) {
return RasterThreadStatus::kRemainsUnmerged;
Expand Down
15 changes: 15 additions & 0 deletions fml/raster_thread_merger.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,29 @@ class RasterThreadMerger
// When the caller merges with a lease term of say 2. The threads
// are going to remain merged until 2 invocations of |DecreaseLease|,
// unless an |ExtendLeaseTo| gets called.
//
// If the task queues are the same, we consider them statically merged.
// When task queues are statically merged this method becomes no-op.
void MergeWithLease(size_t lease_term);

// Un-merges the threads now, and resets the lease term to 0.
//
// Must be executed on the raster task runner.
//
// If the task queues are the same, we consider them statically merged.
// When task queues are statically merged, we never unmerge them and
// this method becomes no-op.
void UnMergeNow();

// If the task queues are the same, we consider them statically merged.
// When task queues are statically merged this method becomes no-op.
void ExtendLeaseTo(size_t lease_term);

// Returns |RasterThreadStatus::kUnmergedNow| if this call resulted in
// splitting the raster and platform threads. Reduces the lease term by 1.
//
// If the task queues are the same, we consider them statically merged.
// When task queues are statically merged this method becomes no-op.
RasterThreadStatus DecrementLease();

bool IsMerged();
Expand Down Expand Up @@ -71,6 +83,9 @@ class RasterThreadMerger
std::mutex lease_term_mutex_;

bool IsMergedUnSafe();
// The platform_queue_id and gpu_queue_id are exactly the same.
// We consider the threads are always merged and cannot be unmerged.
bool TaskQueuesAreSame();

FML_FRIEND_REF_COUNTED_THREAD_SAFE(RasterThreadMerger);
FML_FRIEND_MAKE_REF_COUNTED(RasterThreadMerger);
Expand Down
71 changes: 29 additions & 42 deletions fml/raster_thread_merger_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -262,55 +262,42 @@ TEST(RasterThreadMerger, WaitUntilMerged) {
thread_raster.join();
}

TEST(RasterThreadMerger, WaitUntilMergedReturnsIfAlreadyMerged) {
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger;

fml::AutoResetWaitableEvent thread_merged_latch;
fml::MessageLoop* loop_platform = nullptr;
fml::AutoResetWaitableEvent latch_platform;
fml::AutoResetWaitableEvent term_platform;
fml::AutoResetWaitableEvent latch_wait_until_merged;
std::thread thread_platform([&]() {
TEST(RasterThreadMerger, HandleTaskQueuesAreTheSame) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing checks for ASSERT_TRUE(raster_thread_merger_->TaskQueuesAreSame()), and ASSERT_FALSE(raster_thread_merger_->TaskQueuesAreSame()) in a separate TEST.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make raster_thread_merger_->TaskQueuesAreSame() private. The user of the raster_thread_merger shouldn't need to know if the merging is static or dynamic. So the test only needed to test if the threads are merged.

fml::MessageLoop* loop1 = nullptr;
fml::AutoResetWaitableEvent latch1;
fml::AutoResetWaitableEvent term1;
std::thread thread1([&loop1, &latch1, &term1]() {
fml::MessageLoop::EnsureInitializedForCurrentThread();
loop_platform = &fml::MessageLoop::GetCurrent();
latch_platform.Signal();
thread_merged_latch.Wait();
raster_thread_merger->WaitUntilMerged();
latch_wait_until_merged.Signal();
term_platform.Wait();
loop1 = &fml::MessageLoop::GetCurrent();
latch1.Signal();
term1.Wait();
});

const int 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());
raster_thread_merger->MergeWithLease(kNumFramesMerged);
thread_merged_latch.Signal();
term_raster.Wait();
});
latch1.Wait();

latch_wait_until_merged.Wait();
ASSERT_TRUE(raster_thread_merger->IsMerged());
fml::TaskQueueId qid1 = loop1->GetTaskRunner()->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 int kNumFramesMerged = 5;
raster_thread_merger_->MergeWithLease(kNumFramesMerged);

for (int i = 0; i < kNumFramesMerged; i++) {
ASSERT_TRUE(raster_thread_merger->IsMerged());
raster_thread_merger->DecrementLease();
ASSERT_TRUE(raster_thread_merger_->IsMerged());
raster_thread_merger_->DecrementLease();
}

ASSERT_FALSE(raster_thread_merger->IsMerged());
ASSERT_TRUE(raster_thread_merger_->IsMerged());

term_platform.Signal();
term_raster.Signal();
thread_platform.join();
thread_raster.join();
// Wait until merged should also return immediately.
raster_thread_merger_->WaitUntilMerged();
ASSERT_TRUE(raster_thread_merger_->IsMerged());

term1.Signal();
thread1.join();
}
11 changes: 1 addition & 10 deletions shell/common/rasterizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,7 @@ void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
// TODO(sanjayc77): https://github.com/flutter/flutter/issues/53179. Add
// support for raster thread merger for Fuchsia.
if (surface_->GetExternalViewEmbedder() &&
surface_->GetExternalViewEmbedder()->SupportsDynamicThreadMerging() &&
// Don't create raster_thread_merger if platform and raster task runners
// are the same.
delegate_.GetTaskRunners().GetRasterTaskRunner() !=
delegate_.GetTaskRunners().GetPlatformTaskRunner()) {
surface_->GetExternalViewEmbedder()->SupportsDynamicThreadMerging()) {
const auto platform_id =
delegate_.GetTaskRunners().GetPlatformTaskRunner()->GetTaskQueueId();
const auto gpu_id =
Expand Down Expand Up @@ -668,11 +664,6 @@ std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
}

bool Rasterizer::EnsureThreadsAreMerged() {
// If threads are merged statically, always return true.
if (delegate_.GetTaskRunners().GetRasterTaskRunner() ==
delegate_.GetTaskRunners().GetPlatformTaskRunner()) {
return true;
}
if (surface_ == nullptr || raster_thread_merger_.get() == nullptr) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in what case surface_ = nullptr ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the rasterizer has already been torn down, the surface_ would be null. And in that case we shouldn't try to merge threads or wait the threads to be merged.

return false;
}
Expand Down