Skip to content

Commit 9643c9d

Browse files
authored
Started shutting down the sampler when it gets deleted (flutter#23012)
1 parent 205d2b8 commit 9643c9d

File tree

6 files changed

+107
-4
lines changed

6 files changed

+107
-4
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,7 @@ FILE: ../../../flutter/shell/platform/windows/window_binding_handler_delegate.h
14611461
FILE: ../../../flutter/shell/platform/windows/window_state.h
14621462
FILE: ../../../flutter/shell/profiling/sampling_profiler.cc
14631463
FILE: ../../../flutter/shell/profiling/sampling_profiler.h
1464+
FILE: ../../../flutter/shell/profiling/sampling_profiler_unittest.cc
14641465
FILE: ../../../flutter/shell/version/version.cc
14651466
FILE: ../../../flutter/shell/version/version.h
14661467
FILE: ../../../flutter/sky/packages/flutter_services/lib/empty.dart

shell/common/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ if (enable_unittests) {
254254
":shell_unittests_fixtures",
255255
"//flutter/assets",
256256
"//flutter/common/graphics",
257+
"//flutter/shell/profiling:profiling_unittests",
257258
"//flutter/shell/version",
258259
"//third_party/googletest:gmock",
259260
]

shell/profiling/BUILD.gn

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,12 @@ source_set("profiling") {
1717

1818
deps = _profiler_deps
1919
}
20+
21+
source_set("profiling_unittests") {
22+
testonly = true
23+
sources = [ "sampling_profiler_unittest.cc" ]
24+
deps = [
25+
":profiling",
26+
"//flutter/testing",
27+
]
28+
}

shell/profiling/sampling_profiler.cc

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ SamplingProfiler::SamplingProfiler(
1616
sampler_(std::move(sampler)),
1717
num_samples_per_sec_(num_samples_per_sec) {}
1818

19-
void SamplingProfiler::Start() const {
19+
SamplingProfiler::~SamplingProfiler() {
20+
if (is_running_) {
21+
Stop();
22+
}
23+
}
24+
25+
void SamplingProfiler::Start() {
2026
if (!profiler_task_runner_) {
2127
return;
2228
}
@@ -26,12 +32,23 @@ void SamplingProfiler::Start() const {
2632
double delay_between_samples = 1.0 / num_samples_per_sec_;
2733
auto task_delay = fml::TimeDelta::FromSecondsF(delay_between_samples);
2834
UpdateObservatoryThreadName();
35+
is_running_ = true;
2936
SampleRepeatedly(task_delay);
3037
}
3138

39+
void SamplingProfiler::Stop() {
40+
FML_DCHECK(is_running_);
41+
auto latch = std::make_unique<fml::AutoResetWaitableEvent>();
42+
shutdown_latch_.store(latch.get());
43+
latch->Wait();
44+
shutdown_latch_.store(nullptr);
45+
is_running_ = false;
46+
}
47+
3248
void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const {
3349
profiler_task_runner_->PostDelayedTask(
34-
[profiler = this, task_delay = task_delay, sampler = sampler_]() {
50+
[profiler = this, task_delay = task_delay, sampler = sampler_,
51+
&shutdown_latch = shutdown_latch_]() {
3552
// TODO(kaushikiska): consider buffering these every n seconds to
3653
// avoid spamming the trace buffer.
3754
const ProfileSample usage = sampler();
@@ -60,7 +77,11 @@ void SamplingProfiler::SampleRepeatedly(fml::TimeDelta task_delay) const {
6077
TRACE_EVENT_INSTANT1("flutter::profiling", "GpuUsage", "gpu_usage",
6178
gpu_usage.c_str());
6279
}
63-
profiler->SampleRepeatedly(task_delay);
80+
if (shutdown_latch.load()) {
81+
shutdown_latch.load()->Signal();
82+
} else {
83+
profiler->SampleRepeatedly(task_delay);
84+
}
6485
},
6586
task_delay);
6687
}

shell/profiling/sampling_profiler.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <optional>
1111
#include <string>
1212

13+
#include "flutter/fml/synchronization/count_down_latch.h"
1314
#include "flutter/fml/task_runner.h"
1415
#include "flutter/fml/trace_event.h"
1516

@@ -97,17 +98,23 @@ class SamplingProfiler {
9798
Sampler sampler,
9899
int num_samples_per_sec);
99100

101+
~SamplingProfiler();
102+
100103
/**
101104
* @brief Starts the SamplingProfiler by triggering `SampleRepeatedly`.
102105
*
103106
*/
104-
void Start() const;
107+
void Start();
108+
109+
void Stop();
105110

106111
private:
107112
const std::string thread_label_;
108113
const fml::RefPtr<fml::TaskRunner> profiler_task_runner_;
109114
const Sampler sampler_;
110115
const uint32_t num_samples_per_sec_;
116+
bool is_running_ = false;
117+
std::atomic<fml::AutoResetWaitableEvent*> shutdown_latch_ = nullptr;
111118

112119
void SampleRepeatedly(fml::TimeDelta task_delay) const;
113120

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/shell/profiling/sampling_profiler.h"
6+
#include "flutter/fml/message_loop_impl.h"
7+
#include "flutter/fml/thread.h"
8+
#include "flutter/testing/testing.h"
9+
#include "gmock/gmock.h"
10+
11+
using testing::_;
12+
using testing::Invoke;
13+
14+
namespace fml {
15+
namespace {
16+
class MockTaskRunner : public fml::TaskRunner {
17+
public:
18+
inline static RefPtr<MockTaskRunner> Create() {
19+
return AdoptRef(new MockTaskRunner());
20+
}
21+
MOCK_METHOD1(PostTask, void(const fml::closure& task));
22+
MOCK_METHOD2(PostTaskForTime,
23+
void(const fml::closure& task, fml::TimePoint target_time));
24+
MOCK_METHOD2(PostDelayedTask,
25+
void(const fml::closure& task, fml::TimeDelta delay));
26+
MOCK_METHOD0(RunsTasksOnCurrentThread, bool());
27+
MOCK_METHOD0(GetTaskQueueId, TaskQueueId());
28+
29+
private:
30+
MockTaskRunner() : TaskRunner(fml::RefPtr<MessageLoopImpl>()) {}
31+
};
32+
} // namespace
33+
} // namespace fml
34+
35+
namespace flutter {
36+
37+
TEST(SamplingProfilerTest, DeleteAfterStart) {
38+
auto thread =
39+
std::make_unique<fml::Thread>(flutter::testing::GetCurrentTestName());
40+
auto task_runner = fml::MockTaskRunner::Create();
41+
std::atomic<int> invoke_count = 0;
42+
43+
// Ignore calls to PostTask since that would require mocking out calls to
44+
// Dart.
45+
EXPECT_CALL(*task_runner, PostDelayedTask(_, _))
46+
.WillRepeatedly(
47+
Invoke([&](const fml::closure& task, fml::TimeDelta delay) {
48+
invoke_count.fetch_add(1);
49+
thread->GetTaskRunner()->PostTask(task);
50+
}));
51+
52+
{
53+
auto profiler = SamplingProfiler(
54+
"profiler",
55+
/*profiler_task_runner=*/task_runner, [] { return ProfileSample(); },
56+
/*num_samples_per_sec=*/1000);
57+
profiler.Start();
58+
}
59+
int invoke_count_at_delete = invoke_count.load();
60+
std::this_thread::sleep_for(std::chrono::milliseconds(2)); // nyquist
61+
ASSERT_EQ(invoke_count_at_delete, invoke_count.load());
62+
}
63+
64+
} // namespace flutter

0 commit comments

Comments
 (0)