Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e7136c3

Browse files
author
Chris Yang
authored
Ensure threads are merged when tearing down the Rasterizer (#19919)
1 parent e75b5ed commit e7136c3

14 files changed

Lines changed: 622 additions & 101 deletions

fml/message_loop_task_queues.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ bool MessageLoopTaskQueues::Unmerge(TaskQueueId owner) {
246246
bool MessageLoopTaskQueues::Owns(TaskQueueId owner,
247247
TaskQueueId subsumed) const {
248248
std::lock_guard guard(queue_mutex_);
249-
return subsumed == queue_entries_.at(owner)->owner_of || owner == subsumed;
249+
return subsumed == queue_entries_.at(owner)->owner_of;
250250
}
251251

252252
// Subsumed queues will never have pending tasks.

fml/message_loop_task_queues_unittests.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ TEST(MessageLoopTaskQueue, NotifyObserversWhileCreatingQueues) {
173173
before_second_observer.Signal();
174174
notify_observers.join();
175175
}
176+
177+
TEST(MessageLoopTaskQueue, QueueDoNotOwnItself) {
178+
auto task_queue = fml::MessageLoopTaskQueues::GetInstance();
179+
auto queue_id = task_queue->CreateTaskQueue();
180+
ASSERT_FALSE(task_queue->Owns(queue_id, queue_id));
181+
}
182+
176183
// TODO(chunhtai): This unit-test is flaky and sometimes fails asynchronizely
177184
// after the test has finished.
178185
// https://github.com/flutter/flutter/issues/43858

fml/raster_thread_merger.cc

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,58 +17,97 @@ RasterThreadMerger::RasterThreadMerger(fml::TaskQueueId platform_queue_id,
1717
gpu_queue_id_(gpu_queue_id),
1818
task_queues_(fml::MessageLoopTaskQueues::GetInstance()),
1919
lease_term_(kLeaseNotSet) {
20-
is_merged_ = task_queues_->Owns(platform_queue_id_, gpu_queue_id_);
20+
FML_CHECK(!task_queues_->Owns(platform_queue_id_, gpu_queue_id_));
2121
}
2222

2323
void RasterThreadMerger::MergeWithLease(size_t lease_term) {
24+
if (TaskQueuesAreSame()) {
25+
return;
26+
}
27+
2428
FML_DCHECK(lease_term > 0) << "lease_term should be positive.";
25-
if (!is_merged_) {
26-
is_merged_ = task_queues_->Merge(platform_queue_id_, gpu_queue_id_);
29+
std::scoped_lock lock(lease_term_mutex_);
30+
if (!IsMergedUnSafe()) {
31+
bool success = task_queues_->Merge(platform_queue_id_, gpu_queue_id_);
32+
FML_CHECK(success) << "Unable to merge the raster and platform threads.";
2733
lease_term_ = lease_term;
2834
}
35+
merged_condition_.notify_one();
36+
}
37+
38+
void RasterThreadMerger::UnMergeNow() {
39+
if (TaskQueuesAreSame()) {
40+
return;
41+
}
42+
43+
std::scoped_lock lock(lease_term_mutex_);
44+
lease_term_ = 0;
45+
bool success = task_queues_->Unmerge(platform_queue_id_);
46+
FML_CHECK(success) << "Unable to un-merge the raster and platform threads.";
2947
}
3048

3149
bool RasterThreadMerger::IsOnPlatformThread() const {
3250
return MessageLoop::GetCurrentTaskQueueId() == platform_queue_id_;
3351
}
3452

35-
bool RasterThreadMerger::IsOnRasterizingThread() const {
36-
if (is_merged_) {
53+
bool RasterThreadMerger::IsOnRasterizingThread() {
54+
if (IsMergedUnSafe()) {
3755
return IsOnPlatformThread();
3856
} else {
3957
return !IsOnPlatformThread();
4058
}
4159
}
4260

4361
void RasterThreadMerger::ExtendLeaseTo(size_t lease_term) {
44-
FML_DCHECK(lease_term > 0) << "lease_term should be positive.";
62+
if (TaskQueuesAreSame()) {
63+
return;
64+
}
65+
std::scoped_lock lock(lease_term_mutex_);
66+
FML_DCHECK(IsMergedUnSafe()) << "lease_term should be positive.";
4567
if (lease_term_ != kLeaseNotSet &&
4668
static_cast<int>(lease_term) > lease_term_) {
4769
lease_term_ = lease_term;
4870
}
4971
}
5072

51-
bool RasterThreadMerger::IsMerged() const {
52-
return is_merged_;
73+
bool RasterThreadMerger::IsMerged() {
74+
std::scoped_lock lock(lease_term_mutex_);
75+
return IsMergedUnSafe();
5376
}
5477

55-
RasterThreadStatus RasterThreadMerger::DecrementLease() {
56-
if (!is_merged_) {
57-
return RasterThreadStatus::kRemainsUnmerged;
78+
bool RasterThreadMerger::IsMergedUnSafe() {
79+
return lease_term_ > 0 || TaskQueuesAreSame();
80+
}
81+
82+
bool RasterThreadMerger::TaskQueuesAreSame() {
83+
return platform_queue_id_ == gpu_queue_id_;
84+
}
85+
86+
void RasterThreadMerger::WaitUntilMerged() {
87+
if (TaskQueuesAreSame()) {
88+
return;
5889
}
90+
FML_CHECK(IsOnPlatformThread());
91+
std::unique_lock<std::mutex> lock(lease_term_mutex_);
92+
merged_condition_.wait(lock, [&] { return IsMergedUnSafe(); });
93+
}
5994

60-
// we haven't been set to merge.
61-
if (lease_term_ == kLeaseNotSet) {
95+
RasterThreadStatus RasterThreadMerger::DecrementLease() {
96+
if (TaskQueuesAreSame()) {
97+
return RasterThreadStatus::kRemainsMerged;
98+
}
99+
std::unique_lock<std::mutex> lock(lease_term_mutex_);
100+
if (!IsMergedUnSafe()) {
62101
return RasterThreadStatus::kRemainsUnmerged;
63102
}
64103

65104
FML_DCHECK(lease_term_ > 0)
66105
<< "lease_term should always be positive when merged.";
67106
lease_term_--;
68107
if (lease_term_ == 0) {
69-
bool success = task_queues_->Unmerge(platform_queue_id_);
70-
FML_CHECK(success) << "Unable to un-merge the raster and platform threads.";
71-
is_merged_ = false;
108+
// |UnMergeNow| is going to acquire the lock again.
109+
lock.unlock();
110+
UnMergeNow();
72111
return RasterThreadStatus::kUnmergedNow;
73112
}
74113

fml/raster_thread_merger.h

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef FML_SHELL_COMMON_TASK_RUNNER_MERGER_H_
66
#define FML_SHELL_COMMON_TASK_RUNNER_MERGER_H_
77

8+
#include <condition_variable>
9+
#include <mutex>
810
#include "flutter/fml/macros.h"
911
#include "flutter/fml/memory/ref_counted.h"
1012
#include "flutter/fml/message_loop_task_queues.h"
@@ -28,23 +30,45 @@ class RasterThreadMerger
2830
// When the caller merges with a lease term of say 2. The threads
2931
// are going to remain merged until 2 invocations of |DecreaseLease|,
3032
// unless an |ExtendLeaseTo| gets called.
33+
//
34+
// If the task queues are the same, we consider them statically merged.
35+
// When task queues are statically merged this method becomes no-op.
3136
void MergeWithLease(size_t lease_term);
3237

38+
// Un-merges the threads now, and resets the lease term to 0.
39+
//
40+
// Must be executed on the raster task runner.
41+
//
42+
// If the task queues are the same, we consider them statically merged.
43+
// When task queues are statically merged, we never unmerge them and
44+
// this method becomes no-op.
45+
void UnMergeNow();
46+
47+
// If the task queues are the same, we consider them statically merged.
48+
// When task queues are statically merged this method becomes no-op.
3349
void ExtendLeaseTo(size_t lease_term);
3450

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

39-
bool IsMerged() const;
58+
bool IsMerged();
59+
60+
// Waits until the threads are merged.
61+
//
62+
// Must run on the platform task runner.
63+
void WaitUntilMerged();
4064

4165
RasterThreadMerger(fml::TaskQueueId platform_queue_id,
4266
fml::TaskQueueId gpu_queue_id);
4367

4468
// Returns true if the current thread owns rasterizing.
4569
// When the threads are merged, platform thread owns rasterizing.
4670
// When un-merged, raster thread owns rasterizing.
47-
bool IsOnRasterizingThread() const;
71+
bool IsOnRasterizingThread();
4872

4973
// Returns true if the current thread is the platform thread.
5074
bool IsOnPlatformThread() const;
@@ -55,7 +79,13 @@ class RasterThreadMerger
5579
fml::TaskQueueId gpu_queue_id_;
5680
fml::RefPtr<fml::MessageLoopTaskQueues> task_queues_;
5781
std::atomic_int lease_term_;
58-
bool is_merged_;
82+
std::condition_variable merged_condition_;
83+
std::mutex lease_term_mutex_;
84+
85+
bool IsMergedUnSafe();
86+
// The platform_queue_id and gpu_queue_id are exactly the same.
87+
// We consider the threads are always merged and cannot be unmerged.
88+
bool TaskQueuesAreSame();
5989

6090
FML_FRIEND_REF_COUNTED_THREAD_SAFE(RasterThreadMerger);
6191
FML_FRIEND_MAKE_REF_COUNTED(RasterThreadMerger);

fml/raster_thread_merger_unittests.cc

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,96 @@ TEST(RasterThreadMerger, LeaseExtension) {
208208
thread1.join();
209209
thread2.join();
210210
}
211+
212+
TEST(RasterThreadMerger, WaitUntilMerged) {
213+
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger;
214+
215+
fml::AutoResetWaitableEvent create_thread_merger_latch;
216+
fml::MessageLoop* loop_platform = nullptr;
217+
fml::AutoResetWaitableEvent latch_platform;
218+
fml::AutoResetWaitableEvent term_platform;
219+
fml::AutoResetWaitableEvent latch_merged;
220+
std::thread thread_platform([&]() {
221+
fml::MessageLoop::EnsureInitializedForCurrentThread();
222+
loop_platform = &fml::MessageLoop::GetCurrent();
223+
latch_platform.Signal();
224+
create_thread_merger_latch.Wait();
225+
raster_thread_merger->WaitUntilMerged();
226+
latch_merged.Signal();
227+
term_platform.Wait();
228+
});
229+
230+
const int kNumFramesMerged = 5;
231+
fml::MessageLoop* loop_raster = nullptr;
232+
fml::AutoResetWaitableEvent term_raster;
233+
std::thread thread_raster([&]() {
234+
fml::MessageLoop::EnsureInitializedForCurrentThread();
235+
loop_raster = &fml::MessageLoop::GetCurrent();
236+
latch_platform.Wait();
237+
fml::TaskQueueId qid_platform =
238+
loop_platform->GetTaskRunner()->GetTaskQueueId();
239+
fml::TaskQueueId qid_raster =
240+
loop_raster->GetTaskRunner()->GetTaskQueueId();
241+
raster_thread_merger =
242+
fml::MakeRefCounted<fml::RasterThreadMerger>(qid_platform, qid_raster);
243+
ASSERT_FALSE(raster_thread_merger->IsMerged());
244+
create_thread_merger_latch.Signal();
245+
raster_thread_merger->MergeWithLease(kNumFramesMerged);
246+
term_raster.Wait();
247+
});
248+
249+
latch_merged.Wait();
250+
ASSERT_TRUE(raster_thread_merger->IsMerged());
251+
252+
for (int i = 0; i < kNumFramesMerged; i++) {
253+
ASSERT_TRUE(raster_thread_merger->IsMerged());
254+
raster_thread_merger->DecrementLease();
255+
}
256+
257+
ASSERT_FALSE(raster_thread_merger->IsMerged());
258+
259+
term_platform.Signal();
260+
term_raster.Signal();
261+
thread_platform.join();
262+
thread_raster.join();
263+
}
264+
265+
TEST(RasterThreadMerger, HandleTaskQueuesAreTheSame) {
266+
fml::MessageLoop* loop1 = nullptr;
267+
fml::AutoResetWaitableEvent latch1;
268+
fml::AutoResetWaitableEvent term1;
269+
std::thread thread1([&loop1, &latch1, &term1]() {
270+
fml::MessageLoop::EnsureInitializedForCurrentThread();
271+
loop1 = &fml::MessageLoop::GetCurrent();
272+
latch1.Signal();
273+
term1.Wait();
274+
});
275+
276+
latch1.Wait();
277+
278+
fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
279+
fml::TaskQueueId qid2 = qid1;
280+
const auto raster_thread_merger_ =
281+
fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
282+
// Statically merged.
283+
ASSERT_TRUE(raster_thread_merger_->IsMerged());
284+
285+
// Test decrement lease and unmerge are both no-ops.
286+
// The task queues should be always merged.
287+
const int kNumFramesMerged = 5;
288+
raster_thread_merger_->MergeWithLease(kNumFramesMerged);
289+
290+
for (int i = 0; i < kNumFramesMerged; i++) {
291+
ASSERT_TRUE(raster_thread_merger_->IsMerged());
292+
raster_thread_merger_->DecrementLease();
293+
}
294+
295+
ASSERT_TRUE(raster_thread_merger_->IsMerged());
296+
297+
// Wait until merged should also return immediately.
298+
raster_thread_merger_->WaitUntilMerged();
299+
ASSERT_TRUE(raster_thread_merger_->IsMerged());
300+
301+
term1.Signal();
302+
thread1.join();
303+
}

shell/common/rasterizer.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ void Rasterizer::Teardown() {
7777
compositor_context_->OnGrContextDestroyed();
7878
surface_.reset();
7979
last_layer_tree_.reset();
80+
if (raster_thread_merger_.get() != nullptr &&
81+
raster_thread_merger_.get()->IsMerged()) {
82+
raster_thread_merger_->UnMergeNow();
83+
}
8084
}
8185

8286
void Rasterizer::NotifyLowMemoryWarning() const {
@@ -659,6 +663,24 @@ std::optional<size_t> Rasterizer::GetResourceCacheMaxBytes() const {
659663
return std::nullopt;
660664
}
661665

666+
bool Rasterizer::EnsureThreadsAreMerged() {
667+
if (surface_ == nullptr || raster_thread_merger_.get() == nullptr) {
668+
return false;
669+
}
670+
fml::TaskRunner::RunNowOrPostTask(
671+
delegate_.GetTaskRunners().GetRasterTaskRunner(),
672+
[weak_this = weak_factory_.GetWeakPtr(),
673+
thread_merger = raster_thread_merger_]() {
674+
if (weak_this->surface_ == nullptr) {
675+
return;
676+
}
677+
thread_merger->MergeWithLease(10);
678+
});
679+
raster_thread_merger_->WaitUntilMerged();
680+
FML_DCHECK(raster_thread_merger_->IsMerged());
681+
return true;
682+
}
683+
662684
Rasterizer::Screenshot::Screenshot() {}
663685

664686
Rasterizer::Screenshot::Screenshot(sk_sp<SkData> p_data, SkISize p_size)

shell/common/rasterizer.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,22 @@ class Rasterizer final : public SnapshotDelegate {
389389
///
390390
std::optional<size_t> GetResourceCacheMaxBytes() const;
391391

392+
//----------------------------------------------------------------------------
393+
/// @brief Makes sure the raster task runner and the platform task runner
394+
/// are merged.
395+
///
396+
/// @attention If raster and platform task runners are not the same or not
397+
/// merged, this method will try to merge the task runners,
398+
/// blocking the current thread until the 2 task runners are
399+
/// merged.
400+
///
401+
/// @return `true` if raster and platform task runners are the same.
402+
/// `true` if/when raster and platform task runners are merged.
403+
/// `false` if the surface or the |RasterThreadMerger| has not
404+
/// been initialized.
405+
///
406+
bool EnsureThreadsAreMerged();
407+
392408
private:
393409
Delegate& delegate_;
394410
std::unique_ptr<Surface> surface_;

0 commit comments

Comments
 (0)