From f2c763b56b7acfaf6d03472b7e40ece8fb699424 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Fri, 18 Mar 2022 20:34:14 +0800 Subject: [PATCH 1/6] Fix crash about 'SkiaUnrefQueue::Drain' is called after 'IOManager' reset --- ci/licenses_golden/licenses_flutter | 2 + flow/skia_gpu_object.cc | 2 +- flow/skia_gpu_object.h | 8 +- shell/common/BUILD.gn | 6 +- shell/common/fixtures/hello_loop_2.gif | Bin 0 -> 29328 bytes shell/common/fixtures/shell_test.dart | 5 + shell/common/shell_io_manager.cc | 8 +- shell/common/shell_io_manager.h | 4 +- shell/common/shell_io_manager_unittests.cc | 119 +++++++++++++++++++++ 9 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 shell/common/fixtures/hello_loop_2.gif create mode 100644 shell/common/shell_io_manager_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 87c6208ebcec6..466047019508a 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1050,6 +1050,7 @@ FILE: ../../../flutter/shell/common/display_manager.h FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/engine_unittests.cc +FILE: ../../../flutter/shell/common/fixtures/hello_loop_2.gif FILE: ../../../flutter/shell/common/fixtures/shell_test.dart FILE: ../../../flutter/shell/common/fixtures/shelltest_screenshot.png FILE: ../../../flutter/shell/common/input_events_unittests.cc @@ -1075,6 +1076,7 @@ FILE: ../../../flutter/shell/common/shell_benchmarks.cc FILE: ../../../flutter/shell/common/shell_fuchsia_unittests.cc FILE: ../../../flutter/shell/common/shell_io_manager.cc FILE: ../../../flutter/shell/common/shell_io_manager.h +FILE: ../../../flutter/shell/common/shell_io_manager_unittests.cc FILE: ../../../flutter/shell/common/shell_test.cc FILE: ../../../flutter/shell/common/shell_test.h FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.cc diff --git a/flow/skia_gpu_object.cc b/flow/skia_gpu_object.cc index 7415916d77954..85f8d7c197bf2 100644 --- a/flow/skia_gpu_object.cc +++ b/flow/skia_gpu_object.cc @@ -11,7 +11,7 @@ namespace flutter { SkiaUnrefQueue::SkiaUnrefQueue(fml::RefPtr task_runner, fml::TimeDelta delay, - fml::WeakPtr context) + sk_sp context) : task_runner_(std::move(task_runner)), drain_delay_(delay), drain_pending_(false), diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index 38d29dcacdaf5..d0ff42eec16a8 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -29,20 +29,24 @@ class SkiaUnrefQueue : public fml::RefCountedThreadSafe { // after this call. void Drain(); + void UpdateResourceContext(sk_sp context) { + context_ = context; + } + private: const fml::RefPtr task_runner_; const fml::TimeDelta drain_delay_; std::mutex mutex_; std::deque objects_; bool drain_pending_; - fml::WeakPtr context_; + sk_sp context_; // The `GrDirectContext* context` is only used for signaling Skia to // performDeferredCleanup. It can be nullptr when such signaling is not needed // (e.g., in unit tests). SkiaUnrefQueue(fml::RefPtr task_runner, fml::TimeDelta delay, - fml::WeakPtr context = {}); + sk_sp context = nullptr); ~SkiaUnrefQueue(); diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 50a2881ec0877..1558959223adc 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -159,7 +159,10 @@ if (enable_unittests) { test_fixtures("shell_unittests_fixtures") { dart_main = "fixtures/shell_test.dart" - fixtures = [ "fixtures/shelltest_screenshot.png" ] + fixtures = [ + "fixtures/shelltest_screenshot.png", + "fixtures/hello_loop_2.gif", + ] } shell_host_executable("shell_benchmarks") { @@ -267,6 +270,7 @@ if (enable_unittests) { "persistent_cache_unittests.cc", "pipeline_unittests.cc", "rasterizer_unittests.cc", + "shell_io_manager_unittests.cc", "shell_unittests.cc", "skp_shader_warmup_unittests.cc", "switches_unittests.cc", diff --git a/shell/common/fixtures/hello_loop_2.gif b/shell/common/fixtures/hello_loop_2.gif new file mode 100644 index 0000000000000000000000000000000000000000..e9ac36d917d22f46e0ff15e102580f76cdb6cbf4 GIT binary patch literal 29328 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 literal 0 HcmV?d00001 diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 85c406291c942..f55f0301bf8d3 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -270,3 +270,8 @@ void onBeginFrameWithNotifyNativeMain() { }; notifyNative(); } + +@pragma('vm:entry-point') +void frameCallback(_Image, int) { + print('called back'); +} diff --git a/shell/common/shell_io_manager.cc b/shell/common/shell_io_manager.cc index 115b5efaf7c4d..38ab99d7bce77 100644 --- a/shell/common/shell_io_manager.cc +++ b/shell/common/shell_io_manager.cc @@ -34,7 +34,8 @@ sk_sp ShellIOManager::CreateCompatibleResourceLoadingContext( ShellIOManager::ShellIOManager( sk_sp resource_context, std::shared_ptr is_gpu_disabled_sync_switch, - fml::RefPtr unref_queue_task_runner) + fml::RefPtr unref_queue_task_runner, + fml::TimeDelta unref_queue_drain_delay) : resource_context_(std::move(resource_context)), resource_context_weak_factory_( resource_context_ @@ -43,8 +44,8 @@ ShellIOManager::ShellIOManager( : nullptr), unref_queue_(fml::MakeRefCounted( std::move(unref_queue_task_runner), - fml::TimeDelta::FromMilliseconds(8), - GetResourceContext())), + unref_queue_drain_delay, + resource_context_)), is_gpu_disabled_sync_switch_(is_gpu_disabled_sync_switch), weak_factory_(this) { if (!resource_context_) { @@ -81,6 +82,7 @@ void ShellIOManager::UpdateResourceContext( ? std::make_unique>( resource_context_.get()) : nullptr; + unref_queue_->UpdateResourceContext(resource_context_); } fml::WeakPtr ShellIOManager::GetWeakPtr() { diff --git a/shell/common/shell_io_manager.h b/shell/common/shell_io_manager.h index a6b0b5f137ed7..5f59960ebafd1 100644 --- a/shell/common/shell_io_manager.h +++ b/shell/common/shell_io_manager.h @@ -27,7 +27,9 @@ class ShellIOManager final : public IOManager { ShellIOManager( sk_sp resource_context, std::shared_ptr is_gpu_disabled_sync_switch, - fml::RefPtr unref_queue_task_runner); + fml::RefPtr unref_queue_task_runner, + fml::TimeDelta unref_queue_drain_delay = + fml::TimeDelta::FromMilliseconds(8)); ~ShellIOManager() override; diff --git a/shell/common/shell_io_manager_unittests.cc b/shell/common/shell_io_manager_unittests.cc new file mode 100644 index 0000000000000..04b6d4083b744 --- /dev/null +++ b/shell/common/shell_io_manager_unittests.cc @@ -0,0 +1,119 @@ +// 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. + +#include "flutter/shell/common/shell_io_manager.h" + +#include "flutter/common/task_runners.h" +#include "flutter/fml/mapping.h" +#include "flutter/lib/ui/painting/multi_frame_codec.h" +#include "flutter/testing/dart_isolate_runner.h" +#include "flutter/testing/fixture_test.h" +#include "flutter/testing/post_task_sync.h" +#include "flutter/testing/test_gl_surface.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +static sk_sp OpenFixtureAsSkData(const char* name) { + auto fixtures_directory = + fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead); + if (!fixtures_directory.is_valid()) { + return nullptr; + } + + auto fixture_mapping = + fml::FileMapping::CreateReadOnly(fixtures_directory, name); + + if (!fixture_mapping) { + return nullptr; + } + + SkData::ReleaseProc on_release = [](const void* ptr, void* context) -> void { + delete reinterpret_cast(context); + }; + + auto data = SkData::MakeWithProc(fixture_mapping->GetMapping(), + fixture_mapping->GetSize(), on_release, + fixture_mapping.get()); + + if (!data) { + return nullptr; + } + // The data is now owned by Skia. + fixture_mapping.release(); + return data; +} + +class ShellIOManagerTest : public FixtureTest {}; + +TEST_F(ShellIOManagerTest, + ItDoesNotCrashThatSkiaUnrefQueueDrainAfterIOManagerReset) { + auto settings = CreateSettingsForFixture(); + auto vm_ref = DartVMRef::Create(settings); + auto vm_data = vm_ref.GetVMData(); + auto gif_mapping = OpenFixtureAsSkData("hello_loop_2.gif"); + ASSERT_TRUE(gif_mapping); + + ImageGeneratorRegistry registry; + std::shared_ptr gif_generator = + registry.CreateCompatibleGenerator(gif_mapping); + ASSERT_TRUE(gif_generator); + + TaskRunners runners(GetCurrentTestName(), // label + CreateNewThread("platform"), // platform + CreateNewThread("raster"), // raster + CreateNewThread("ui"), // ui + CreateNewThread("io") // io + ); + + std::unique_ptr gl_surface; + std::unique_ptr io_manager; + fml::RefPtr codec; + + // Setup the IO manager. + PostTaskSync(runners.GetIOTaskRunner(), [&]() { + gl_surface = std::make_unique(SkISize::Make(1, 1)); + io_manager = std::make_unique( + gl_surface->CreateGrContext(), std::make_shared(), + runners.GetIOTaskRunner(), fml::TimeDelta::FromMilliseconds(0)); + }); + + auto isolate = RunDartCodeInIsolate(vm_ref, settings, runners, "emptyMain", + {}, GetDefaultKernelFilePath(), + io_manager->GetWeakIOManager()); + + PostTaskSync(runners.GetUITaskRunner(), [&]() { + fml::AutoResetWaitableEvent isolate_latch; + + EXPECT_TRUE(isolate->RunInIsolateScope([&]() -> bool { + Dart_Handle library = Dart_RootLibrary(); + if (Dart_IsError(library)) { + isolate_latch.Signal(); + return false; + } + Dart_Handle closure = + Dart_GetField(library, Dart_NewStringFromCString("frameCallback")); + if (Dart_IsError(closure) || !Dart_IsClosure(closure)) { + isolate_latch.Signal(); + return false; + } + + codec = fml::MakeRefCounted(std::move(gif_generator)); + codec->getNextFrame(closure); + isolate_latch.Signal(); + return true; + })); + isolate_latch.Wait(); + }); + + // Destroy the IO manager + PostTaskSync(runners.GetIOTaskRunner(), [&]() { + io_manager.reset(); + gl_surface.reset(); + }); +} + +} // namespace testing +} // namespace flutter From 50e7cb0030eb77630abbe2c67b7463ca806f5cab Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Fri, 18 Mar 2022 20:53:02 +0800 Subject: [PATCH 2/6] disable shell_io_manager_unittests on fuchsia --- shell/common/BUILD.gn | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 1558959223adc..d3bc759817a40 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -270,7 +270,6 @@ if (enable_unittests) { "persistent_cache_unittests.cc", "pipeline_unittests.cc", "rasterizer_unittests.cc", - "shell_io_manager_unittests.cc", "shell_unittests.cc", "skp_shader_warmup_unittests.cc", "switches_unittests.cc", @@ -297,6 +296,9 @@ if (enable_unittests) { "$fuchsia_sdk_root/pkg:fidl_cpp", "$fuchsia_sdk_root/pkg:sys_cpp", ] + } else { + # TODO(63837): This test is hard-coded to use a TestGLSurface so it cannot run on fuchsia. + sources += [ "shell_io_manager_unittests.cc" ] } } } From 4dcd59926e251f2131865f0019378c3c65ae8900 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Fri, 18 Mar 2022 21:09:21 +0800 Subject: [PATCH 3/6] Add some comments --- shell/common/shell_io_manager_unittests.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/common/shell_io_manager_unittests.cc b/shell/common/shell_io_manager_unittests.cc index 04b6d4083b744..a2e4c363cb10d 100644 --- a/shell/common/shell_io_manager_unittests.cc +++ b/shell/common/shell_io_manager_unittests.cc @@ -48,6 +48,7 @@ static sk_sp OpenFixtureAsSkData(const char* name) { class ShellIOManagerTest : public FixtureTest {}; +// Regression test for https://github.com/flutter/engine/pull/32106. TEST_F(ShellIOManagerTest, ItDoesNotCrashThatSkiaUnrefQueueDrainAfterIOManagerReset) { auto settings = CreateSettingsForFixture(); @@ -110,6 +111,10 @@ TEST_F(ShellIOManagerTest, // Destroy the IO manager PostTaskSync(runners.GetIOTaskRunner(), [&]() { + // 'SkiaUnrefQueue.Drain' will be called after 'io_manager.reset()' in this + // test, If the resource context has been destroyed at that time, it will + // crash. + // See https://github.com/flutter/flutter/issues/87895 io_manager.reset(); gl_surface.reset(); }); From 4a2ea8022d1ab286bf25f4a1c83e2682f684daa5 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Sat, 19 Mar 2022 12:17:04 +0800 Subject: [PATCH 4/6] The resource 'GrDirectContext' should be destructed the IO thread --- flow/skia_gpu_object.cc | 16 +++++++++++++--- flow/skia_gpu_object.h | 3 +++ shell/common/fixtures/shell_test.dart | 4 +++- shell/common/shell_io_manager_unittests.cc | 11 +++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/flow/skia_gpu_object.cc b/flow/skia_gpu_object.cc index 85f8d7c197bf2..fbd08c6c8dca4 100644 --- a/flow/skia_gpu_object.cc +++ b/flow/skia_gpu_object.cc @@ -18,7 +18,12 @@ SkiaUnrefQueue::SkiaUnrefQueue(fml::RefPtr task_runner, context_(context) {} SkiaUnrefQueue::~SkiaUnrefQueue() { - FML_DCHECK(objects_.empty()); + fml::TaskRunner::RunNowOrPostTask( + task_runner_, + [objects = std::move(objects_), context = std::move(context_)]() mutable { + DoDrain(objects, context); + context.reset(); + }); } void SkiaUnrefQueue::Unref(SkRefCnt* object) { @@ -39,13 +44,18 @@ void SkiaUnrefQueue::Drain() { objects_.swap(skia_objects); drain_pending_ = false; } + DoDrain(skia_objects, context_); +} +// static +void SkiaUnrefQueue::DoDrain(const std::deque& skia_objects, + sk_sp context) { for (SkRefCnt* skia_object : skia_objects) { skia_object->unref(); } - if (context_ && skia_objects.size() > 0) { - context_->performDeferredCleanup(std::chrono::milliseconds(0)); + if (context && skia_objects.size() > 0) { + context->performDeferredCleanup(std::chrono::milliseconds(0)); } } diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index d0ff42eec16a8..e6b7e6f6b485d 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -50,6 +50,9 @@ class SkiaUnrefQueue : public fml::RefCountedThreadSafe { ~SkiaUnrefQueue(); + static void DoDrain(const std::deque& skia_objects, + sk_sp context); + FML_FRIEND_REF_COUNTED_THREAD_SAFE(SkiaUnrefQueue); FML_FRIEND_MAKE_REF_COUNTED(SkiaUnrefQueue); FML_DISALLOW_COPY_AND_ASSIGN(SkiaUnrefQueue); diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index f55f0301bf8d3..e2e9c2f08a0fd 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -273,5 +273,7 @@ void onBeginFrameWithNotifyNativeMain() { @pragma('vm:entry-point') void frameCallback(_Image, int) { - print('called back'); + // It is used as the frame callback of 'MultiFrameCodec' in the test + // 'ItDoesNotCrashThatSkiaUnrefQueueDrainAfterIOManagerReset'. + // The test is a regression test and doesn't care about images, so it is empty. } diff --git a/shell/common/shell_io_manager_unittests.cc b/shell/common/shell_io_manager_unittests.cc index a2e4c363cb10d..2387e0b7e2764 100644 --- a/shell/common/shell_io_manager_unittests.cc +++ b/shell/common/shell_io_manager_unittests.cc @@ -114,6 +114,17 @@ TEST_F(ShellIOManagerTest, // 'SkiaUnrefQueue.Drain' will be called after 'io_manager.reset()' in this // test, If the resource context has been destroyed at that time, it will // crash. + // + // 'Drain()' currently checks whether the weak pointer is still valid or not + // before trying to call anything on it. + // + // However, calling 'unref' on the 'SkImage_Lazy' ends up freeing a + // 'GrBackendTexture'. That object seems to assume that something else is + // keeping the context alive. This seems like it might be a bad assumption + // on Skia's part, but in Skia's defense we're doing something pretty weird + // here by keeping GPU resident objects alive without keeping the + // 'GrDirectContext' alive ourselves. + // // See https://github.com/flutter/flutter/issues/87895 io_manager.reset(); gl_surface.reset(); From 44be8cded39ee11fa146cf4427e4f9a0a8c141c2 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Sun, 20 Mar 2022 21:43:08 +0800 Subject: [PATCH 5/6] Add a new test to verify that the resource context is destroyed in the task runner's thread --- ci/licenses_golden/licenses_flutter | 1 - flow/BUILD.gn | 1 - flow/skia_gpu_object.cc | 62 ------------------------- flow/skia_gpu_object.h | 71 +++++++++++++++++++++++------ flow/skia_gpu_object_unittests.cc | 33 +++++++++++++- 5 files changed, 89 insertions(+), 79 deletions(-) delete mode 100644 flow/skia_gpu_object.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 466047019508a..89cadedbc7d91 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -170,7 +170,6 @@ FILE: ../../../flutter/flow/raster_cache_unittests.cc FILE: ../../../flutter/flow/rtree.cc FILE: ../../../flutter/flow/rtree.h FILE: ../../../flutter/flow/rtree_unittests.cc -FILE: ../../../flutter/flow/skia_gpu_object.cc FILE: ../../../flutter/flow/skia_gpu_object.h FILE: ../../../flutter/flow/skia_gpu_object_unittests.cc FILE: ../../../flutter/flow/surface.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index def9e48d62c76..57164dfb13085 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -64,7 +64,6 @@ source_set("flow") { "raster_cache_key.h", "rtree.cc", "rtree.h", - "skia_gpu_object.cc", "skia_gpu_object.h", "surface.cc", "surface.h", diff --git a/flow/skia_gpu_object.cc b/flow/skia_gpu_object.cc deleted file mode 100644 index fbd08c6c8dca4..0000000000000 --- a/flow/skia_gpu_object.cc +++ /dev/null @@ -1,62 +0,0 @@ -// 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. - -#include "flutter/flow/skia_gpu_object.h" - -#include "flutter/fml/message_loop.h" -#include "flutter/fml/trace_event.h" - -namespace flutter { - -SkiaUnrefQueue::SkiaUnrefQueue(fml::RefPtr task_runner, - fml::TimeDelta delay, - sk_sp context) - : task_runner_(std::move(task_runner)), - drain_delay_(delay), - drain_pending_(false), - context_(context) {} - -SkiaUnrefQueue::~SkiaUnrefQueue() { - fml::TaskRunner::RunNowOrPostTask( - task_runner_, - [objects = std::move(objects_), context = std::move(context_)]() mutable { - DoDrain(objects, context); - context.reset(); - }); -} - -void SkiaUnrefQueue::Unref(SkRefCnt* object) { - std::scoped_lock lock(mutex_); - objects_.push_back(object); - if (!drain_pending_) { - drain_pending_ = true; - task_runner_->PostDelayedTask( - [strong = fml::Ref(this)]() { strong->Drain(); }, drain_delay_); - } -} - -void SkiaUnrefQueue::Drain() { - TRACE_EVENT0("flutter", "SkiaUnrefQueue::Drain"); - std::deque skia_objects; - { - std::scoped_lock lock(mutex_); - objects_.swap(skia_objects); - drain_pending_ = false; - } - DoDrain(skia_objects, context_); -} - -// static -void SkiaUnrefQueue::DoDrain(const std::deque& skia_objects, - sk_sp context) { - for (SkRefCnt* skia_object : skia_objects) { - skia_object->unref(); - } - - if (context && skia_objects.size() > 0) { - context->performDeferredCleanup(std::chrono::milliseconds(0)); - } -} - -} // namespace flutter diff --git a/flow/skia_gpu_object.h b/flow/skia_gpu_object.h index e6b7e6f6b485d..7eb067d7dd48e 100644 --- a/flow/skia_gpu_object.h +++ b/flow/skia_gpu_object.h @@ -11,6 +11,7 @@ #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/task_runner.h" +#include "flutter/fml/trace_event.h" #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -18,18 +19,38 @@ namespace flutter { // A queue that holds Skia objects that must be destructed on the given task // runner. -class SkiaUnrefQueue : public fml::RefCountedThreadSafe { +template +class UnrefQueue : public fml::RefCountedThreadSafe> { public: - void Unref(SkRefCnt* object); + using ResourceContext = T; + + void Unref(SkRefCnt* object) { + std::scoped_lock lock(mutex_); + objects_.push_back(object); + if (!drain_pending_) { + drain_pending_ = true; + task_runner_->PostDelayedTask( + [strong = fml::Ref(this)]() { strong->Drain(); }, drain_delay_); + } + } // Usually, the drain is called automatically. However, during IO manager // shutdown (when the platform side reference to the OpenGL context is about // to go away), we may need to pre-emptively drain the unref queue. It is the // responsibility of the caller to ensure that no further unrefs are queued // after this call. - void Drain(); + void Drain() { + TRACE_EVENT0("flutter", "SkiaUnrefQueue::Drain"); + std::deque skia_objects; + { + std::scoped_lock lock(mutex_); + objects_.swap(skia_objects); + drain_pending_ = false; + } + DoDrain(skia_objects, context_); + } - void UpdateResourceContext(sk_sp context) { + void UpdateResourceContext(sk_sp context) { context_ = context; } @@ -39,25 +60,47 @@ class SkiaUnrefQueue : public fml::RefCountedThreadSafe { std::mutex mutex_; std::deque objects_; bool drain_pending_; - sk_sp context_; + sk_sp context_; // The `GrDirectContext* context` is only used for signaling Skia to // performDeferredCleanup. It can be nullptr when such signaling is not needed // (e.g., in unit tests). - SkiaUnrefQueue(fml::RefPtr task_runner, - fml::TimeDelta delay, - sk_sp context = nullptr); - - ~SkiaUnrefQueue(); + UnrefQueue(fml::RefPtr task_runner, + fml::TimeDelta delay, + sk_sp context = nullptr) + : task_runner_(std::move(task_runner)), + drain_delay_(delay), + drain_pending_(false), + context_(context) {} + + ~UnrefQueue() { + fml::TaskRunner::RunNowOrPostTask( + task_runner_, [objects = std::move(objects_), + context = std::move(context_)]() mutable { + DoDrain(objects, context); + context.reset(); + }); + } + // static static void DoDrain(const std::deque& skia_objects, - sk_sp context); + sk_sp context) { + for (SkRefCnt* skia_object : skia_objects) { + skia_object->unref(); + } + + if (context && skia_objects.size() > 0) { + context->performDeferredCleanup(std::chrono::milliseconds(0)); + } + } - FML_FRIEND_REF_COUNTED_THREAD_SAFE(SkiaUnrefQueue); - FML_FRIEND_MAKE_REF_COUNTED(SkiaUnrefQueue); - FML_DISALLOW_COPY_AND_ASSIGN(SkiaUnrefQueue); + FML_FRIEND_REF_COUNTED_THREAD_SAFE(UnrefQueue); + FML_FRIEND_MAKE_REF_COUNTED(UnrefQueue); + FML_DISALLOW_COPY_AND_ASSIGN(UnrefQueue); }; +using SkiaUnrefQueue = UnrefQueue; + /// An object whose deallocation needs to be performed on an specific unref /// queue. The template argument U need to have a call operator that returns /// that unref queue. diff --git a/flow/skia_gpu_object_unittests.cc b/flow/skia_gpu_object_unittests.cc index faf0f3acec0ab..50e9713c94a58 100644 --- a/flow/skia_gpu_object_unittests.cc +++ b/flow/skia_gpu_object_unittests.cc @@ -22,7 +22,7 @@ class TestSkObject : public SkRefCnt { fml::TaskQueueId* dtor_task_queue_id) : latch_(latch), dtor_task_queue_id_(dtor_task_queue_id) {} - ~TestSkObject() { + virtual ~TestSkObject() { if (dtor_task_queue_id_) { *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); } @@ -34,6 +34,15 @@ class TestSkObject : public SkRefCnt { fml::TaskQueueId* dtor_task_queue_id_; }; +class TestResourceContext : public TestSkObject { + public: + TestResourceContext(std::shared_ptr latch, + fml::TaskQueueId* dtor_task_queue_id) + : TestSkObject(latch, dtor_task_queue_id) {} + ~TestResourceContext() = default; + void performDeferredCleanup(std::chrono::milliseconds msNotUsed) {} +}; + class SkiaGpuObjectTest : public ThreadTest { public: SkiaGpuObjectTest() @@ -127,5 +136,27 @@ TEST_F(SkiaGpuObjectTest, ObjectResetTwice) { ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); } +TEST_F(SkiaGpuObjectTest, UnrefResourceContextInTaskRunnerThread) { + std::shared_ptr latch = + std::make_shared(); + fml::RefPtr> unref_queue; + fml::TaskQueueId dtor_task_queue_id(0); + unref_task_runner()->PostTask([&]() mutable { + auto resource_context = + sk_make_sp(latch, &dtor_task_queue_id); + unref_queue = fml::MakeRefCounted>( + unref_task_runner(), fml::TimeDelta::FromSeconds(0), resource_context); + latch->Signal(); + }); + latch->Wait(); + + // Delete the unref queue, it will schedule a task to unref the resource + // context in the task runner's thread. + unref_queue = nullptr; + latch->Wait(); + // Verify that the resource context was destroyed in the task runner's thread. + ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); +} + } // namespace testing } // namespace flutter From ace9be6168ebb82e3a5ac9baa71c4d39172e9377 Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Mon, 21 Mar 2022 10:54:14 +0800 Subject: [PATCH 6/6] remove unnecessary 'mutable' in test --- flow/skia_gpu_object_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/skia_gpu_object_unittests.cc b/flow/skia_gpu_object_unittests.cc index 50e9713c94a58..cda91dd3f6e2b 100644 --- a/flow/skia_gpu_object_unittests.cc +++ b/flow/skia_gpu_object_unittests.cc @@ -141,7 +141,7 @@ TEST_F(SkiaGpuObjectTest, UnrefResourceContextInTaskRunnerThread) { std::make_shared(); fml::RefPtr> unref_queue; fml::TaskQueueId dtor_task_queue_id(0); - unref_task_runner()->PostTask([&]() mutable { + unref_task_runner()->PostTask([&]() { auto resource_context = sk_make_sp(latch, &dtor_task_queue_id); unref_queue = fml::MakeRefCounted>(