mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
We now have one mutex guarding all accesses to the underlying task heaps. This simplifies the more granular but bug prone mechanism of having striped locks. This also re-enables GPUThreadMerger tests that are currently disabled due to their flaky nature. The scenario that gets fixed by this change is as follows: 1. Thread-1: We lock `queue_meta_mutex_` and grab locks on `queue_1` and release the meta mutex. 2. Thread-1: We add an Observer on `queues` object. 3. Thread-2: We lock `queue_meta_mutex_` and grab locks on `queue_2`. 4. Thread-2: We try to dispose all the pending tasks on `queue_2` which calls `erase` on `queues`. The above situation is not thread safe without having 1 lock. Note: This increases the contention on one lock and could potentially be bad for perf. We are explicitly making this trade-off towards reducing the complexity. Fixes: https://github.com/flutter/flutter/issues/49007
205 lines
6.0 KiB
C++
205 lines
6.0 KiB
C++
// 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 <atomic>
|
|
#include <thread>
|
|
|
|
#include "flutter/fml/gpu_thread_merger.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 "gtest/gtest.h"
|
|
|
|
TEST(GpuThreadMerger, RemainMergedTillLeaseExpires) {
|
|
fml::MessageLoop* loop1 = nullptr;
|
|
fml::AutoResetWaitableEvent latch1;
|
|
fml::AutoResetWaitableEvent term1;
|
|
std::thread thread1([&loop1, &latch1, &term1]() {
|
|
fml::MessageLoop::EnsureInitializedForCurrentThread();
|
|
loop1 = &fml::MessageLoop::GetCurrent();
|
|
latch1.Signal();
|
|
term1.Wait();
|
|
});
|
|
|
|
fml::MessageLoop* loop2 = nullptr;
|
|
fml::AutoResetWaitableEvent latch2;
|
|
fml::AutoResetWaitableEvent term2;
|
|
std::thread thread2([&loop2, &latch2, &term2]() {
|
|
fml::MessageLoop::EnsureInitializedForCurrentThread();
|
|
loop2 = &fml::MessageLoop::GetCurrent();
|
|
latch2.Signal();
|
|
term2.Wait();
|
|
});
|
|
|
|
latch1.Wait();
|
|
latch2.Wait();
|
|
|
|
fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
|
|
fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
|
|
const auto gpu_thread_merger_ =
|
|
fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
|
|
const int kNumFramesMerged = 5;
|
|
|
|
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
|
|
|
|
gpu_thread_merger_->MergeWithLease(kNumFramesMerged);
|
|
|
|
for (int i = 0; i < kNumFramesMerged; i++) {
|
|
ASSERT_TRUE(gpu_thread_merger_->IsMerged());
|
|
gpu_thread_merger_->DecrementLease();
|
|
}
|
|
|
|
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
|
|
|
|
term1.Signal();
|
|
term2.Signal();
|
|
thread1.join();
|
|
thread2.join();
|
|
}
|
|
|
|
TEST(GpuThreadMerger, 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 gpu_thread_merger_ =
|
|
fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
|
|
|
|
fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2);
|
|
|
|
loop1->GetTaskRunner()->PostTask([&]() {
|
|
ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread());
|
|
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
|
|
pre_merge.CountDown();
|
|
});
|
|
|
|
loop2->GetTaskRunner()->PostTask([&]() {
|
|
ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
|
|
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
|
|
pre_merge.CountDown();
|
|
});
|
|
|
|
pre_merge.Wait();
|
|
|
|
gpu_thread_merger_->MergeWithLease(1);
|
|
|
|
loop1->GetTaskRunner()->PostTask([&]() {
|
|
ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
|
|
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(gpu_thread_merger_->IsOnRasterizingThread());
|
|
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
|
|
post_merge.CountDown();
|
|
});
|
|
|
|
post_merge.Wait();
|
|
|
|
gpu_thread_merger_->DecrementLease();
|
|
|
|
loop1->GetTaskRunner()->PostTask([&]() {
|
|
ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread());
|
|
ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
|
|
post_unmerge.CountDown();
|
|
});
|
|
|
|
loop2->GetTaskRunner()->PostTask([&]() {
|
|
ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread());
|
|
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(GpuThreadMerger, LeaseExtension) {
|
|
fml::MessageLoop* loop1 = nullptr;
|
|
fml::AutoResetWaitableEvent latch1;
|
|
fml::AutoResetWaitableEvent term1;
|
|
std::thread thread1([&loop1, &latch1, &term1]() {
|
|
fml::MessageLoop::EnsureInitializedForCurrentThread();
|
|
loop1 = &fml::MessageLoop::GetCurrent();
|
|
latch1.Signal();
|
|
term1.Wait();
|
|
});
|
|
|
|
fml::MessageLoop* loop2 = nullptr;
|
|
fml::AutoResetWaitableEvent latch2;
|
|
fml::AutoResetWaitableEvent term2;
|
|
std::thread thread2([&loop2, &latch2, &term2]() {
|
|
fml::MessageLoop::EnsureInitializedForCurrentThread();
|
|
loop2 = &fml::MessageLoop::GetCurrent();
|
|
latch2.Signal();
|
|
term2.Wait();
|
|
});
|
|
|
|
latch1.Wait();
|
|
latch2.Wait();
|
|
|
|
fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
|
|
fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
|
|
const auto gpu_thread_merger_ =
|
|
fml::MakeRefCounted<fml::GpuThreadMerger>(qid1, qid2);
|
|
const int kNumFramesMerged = 5;
|
|
|
|
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
|
|
|
|
gpu_thread_merger_->MergeWithLease(kNumFramesMerged);
|
|
|
|
// let there be one more turn till the leases expire.
|
|
for (int i = 0; i < kNumFramesMerged - 1; i++) {
|
|
ASSERT_TRUE(gpu_thread_merger_->IsMerged());
|
|
gpu_thread_merger_->DecrementLease();
|
|
}
|
|
|
|
// extend the lease once.
|
|
gpu_thread_merger_->ExtendLeaseTo(kNumFramesMerged);
|
|
|
|
// we will NOT last for 1 extra turn, we just set it.
|
|
for (int i = 0; i < kNumFramesMerged; i++) {
|
|
ASSERT_TRUE(gpu_thread_merger_->IsMerged());
|
|
gpu_thread_merger_->DecrementLease();
|
|
}
|
|
|
|
ASSERT_FALSE(gpu_thread_merger_->IsMerged());
|
|
|
|
term1.Signal();
|
|
term2.Signal();
|
|
thread1.join();
|
|
thread2.join();
|
|
}
|