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

Commit 3f4c503

Browse files
authored
Print a warning when a message channel is used on the wrong thread. (#42928)
Fixes flutter/flutter#128746 Prints a warning the first time a platform channel sends a message from the wrong thread with instructions/link to the site about how to fix this.
1 parent 10a9940 commit 3f4c503

File tree

11 files changed

+175
-46
lines changed

11 files changed

+175
-46
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ ORIGIN: ../../../flutter/fml/memory/ref_ptr.h + ../../../flutter/LICENSE
899899
ORIGIN: ../../../flutter/fml/memory/ref_ptr_internal.h + ../../../flutter/LICENSE
900900
ORIGIN: ../../../flutter/fml/memory/task_runner_checker.cc + ../../../flutter/LICENSE
901901
ORIGIN: ../../../flutter/fml/memory/task_runner_checker.h + ../../../flutter/LICENSE
902+
ORIGIN: ../../../flutter/fml/memory/thread_checker.cc + ../../../flutter/LICENSE
902903
ORIGIN: ../../../flutter/fml/memory/thread_checker.h + ../../../flutter/LICENSE
903904
ORIGIN: ../../../flutter/fml/memory/weak_ptr.h + ../../../flutter/LICENSE
904905
ORIGIN: ../../../flutter/fml/memory/weak_ptr_internal.cc + ../../../flutter/LICENSE
@@ -3577,6 +3578,7 @@ FILE: ../../../flutter/fml/memory/ref_ptr.h
35773578
FILE: ../../../flutter/fml/memory/ref_ptr_internal.h
35783579
FILE: ../../../flutter/fml/memory/task_runner_checker.cc
35793580
FILE: ../../../flutter/fml/memory/task_runner_checker.h
3581+
FILE: ../../../flutter/fml/memory/thread_checker.cc
35803582
FILE: ../../../flutter/fml/memory/thread_checker.h
35813583
FILE: ../../../flutter/fml/memory/weak_ptr.h
35823584
FILE: ../../../flutter/fml/memory/weak_ptr_internal.cc

fml/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ source_set("fml") {
4747
"memory/ref_ptr_internal.h",
4848
"memory/task_runner_checker.cc",
4949
"memory/task_runner_checker.h",
50+
"memory/thread_checker.cc",
5051
"memory/thread_checker.h",
5152
"memory/weak_ptr.h",
5253
"memory/weak_ptr_internal.cc",

fml/logging.cc

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -72,61 +72,78 @@ LogMessage::LogMessage(LogSeverity severity,
7272
}
7373
}
7474

75+
// static
76+
thread_local std::ostringstream* LogMessage::capture_next_log_stream_ = nullptr;
77+
78+
void CaptureNextLog(std::ostringstream* stream) {
79+
LogMessage::CaptureNextLog(stream);
80+
}
81+
82+
// static
83+
void LogMessage::CaptureNextLog(std::ostringstream* stream) {
84+
LogMessage::capture_next_log_stream_ = stream;
85+
}
86+
7587
LogMessage::~LogMessage() {
7688
#if !defined(OS_FUCHSIA)
7789
stream_ << std::endl;
7890
#endif
7991

92+
if (capture_next_log_stream_) {
93+
*capture_next_log_stream_ << stream_.str();
94+
capture_next_log_stream_ = nullptr;
95+
} else {
8096
#if defined(FML_OS_ANDROID)
81-
android_LogPriority priority =
82-
(severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
83-
switch (severity_) {
84-
case LOG_INFO:
85-
priority = ANDROID_LOG_INFO;
86-
break;
87-
case LOG_WARNING:
88-
priority = ANDROID_LOG_WARN;
89-
break;
90-
case LOG_ERROR:
91-
priority = ANDROID_LOG_ERROR;
92-
break;
93-
case LOG_FATAL:
94-
priority = ANDROID_LOG_FATAL;
95-
break;
96-
}
97-
__android_log_write(priority, "flutter", stream_.str().c_str());
97+
android_LogPriority priority =
98+
(severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
99+
switch (severity_) {
100+
case LOG_INFO:
101+
priority = ANDROID_LOG_INFO;
102+
break;
103+
case LOG_WARNING:
104+
priority = ANDROID_LOG_WARN;
105+
break;
106+
case LOG_ERROR:
107+
priority = ANDROID_LOG_ERROR;
108+
break;
109+
case LOG_FATAL:
110+
priority = ANDROID_LOG_FATAL;
111+
break;
112+
}
113+
__android_log_write(priority, "flutter", stream_.str().c_str());
98114
#elif defined(FML_OS_IOS)
99-
syslog(LOG_ALERT, "%s", stream_.str().c_str());
115+
syslog(LOG_ALERT, "%s", stream_.str().c_str());
100116
#elif defined(OS_FUCHSIA)
101-
fx_log_severity_t fx_severity;
102-
switch (severity_) {
103-
case LOG_INFO:
104-
fx_severity = FX_LOG_INFO;
105-
break;
106-
case LOG_WARNING:
107-
fx_severity = FX_LOG_WARNING;
108-
break;
109-
case LOG_ERROR:
110-
fx_severity = FX_LOG_ERROR;
111-
break;
112-
case LOG_FATAL:
113-
fx_severity = FX_LOG_FATAL;
114-
break;
115-
default:
116-
if (severity_ < 0) {
117-
fx_severity = fx_log_severity_from_verbosity(-severity_);
118-
} else {
119-
// Unknown severity. Use INFO.
117+
fx_log_severity_t fx_severity;
118+
switch (severity_) {
119+
case LOG_INFO:
120120
fx_severity = FX_LOG_INFO;
121-
}
122-
}
123-
fx_logger_log_with_source(fx_log_get_logger(), fx_severity, nullptr, file_,
124-
line_, stream_.str().c_str());
121+
break;
122+
case LOG_WARNING:
123+
fx_severity = FX_LOG_WARNING;
124+
break;
125+
case LOG_ERROR:
126+
fx_severity = FX_LOG_ERROR;
127+
break;
128+
case LOG_FATAL:
129+
fx_severity = FX_LOG_FATAL;
130+
break;
131+
default:
132+
if (severity_ < 0) {
133+
fx_severity = fx_log_severity_from_verbosity(-severity_);
134+
} else {
135+
// Unknown severity. Use INFO.
136+
fx_severity = FX_LOG_INFO;
137+
}
138+
}
139+
fx_logger_log_with_source(fx_log_get_logger(), fx_severity, nullptr, file_,
140+
line_, stream_.str().c_str());
125141
#else
126-
// Don't use std::cerr here, because it may not be initialized properly yet.
127-
fprintf(stderr, "%s", stream_.str().c_str());
128-
fflush(stderr);
142+
// Don't use std::cerr here, because it may not be initialized properly yet.
143+
fprintf(stderr, "%s", stream_.str().c_str());
144+
fflush(stderr);
129145
#endif
146+
}
130147

131148
if (severity_ >= LOG_FATAL) {
132149
KillProcess();

fml/logging.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
namespace fml {
1414

15+
void CaptureNextLog(std::ostringstream* stream);
16+
1517
class LogMessageVoidify {
1618
public:
1719
void operator&(std::ostream&) {}
@@ -27,7 +29,12 @@ class LogMessage {
2729

2830
std::ostream& stream() { return stream_; }
2931

32+
static void CaptureNextLog(std::ostringstream* stream);
33+
3034
private:
35+
// This is a raw pointer so that we avoid having a non-trivially-destructible
36+
// static. It is only ever for use in unit tests.
37+
static thread_local std::ostringstream* capture_next_log_stream_;
3138
std::ostringstream stream_;
3239
const LogSeverity severity_;
3340
const char* file_;

fml/memory/thread_checker.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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/fml/memory/thread_checker.h"
6+
7+
namespace fml {
8+
9+
thread_local bool ThreadChecker::disable_next_failure_ = false;
10+
11+
}

fml/memory/thread_checker.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,25 @@ namespace fml {
2929
// |CalledOnValidThread()| that lies in Release builds seems bad. Moreover,
3030
// there's a small space cost to having even an empty class. )
3131
class ThreadChecker final {
32+
public:
33+
static void DisableNextThreadCheckFailure() { disable_next_failure_ = true; }
34+
35+
private:
36+
static thread_local bool disable_next_failure_;
37+
3238
public:
3339
#if defined(FML_OS_WIN)
3440
ThreadChecker() : self_(GetCurrentThreadId()) {}
3541
~ThreadChecker() {}
3642

37-
bool IsCreationThreadCurrent() const { return GetCurrentThreadId() == self_; }
43+
bool IsCreationThreadCurrent() const {
44+
bool result = GetCurrentThreadId() == self_;
45+
if (!result && disable_next_failure_) {
46+
disable_next_failure_ = false;
47+
return true;
48+
}
49+
return result;
50+
}
3851

3952
private:
4053
DWORD self_;
@@ -48,6 +61,10 @@ class ThreadChecker final {
4861
bool IsCreationThreadCurrent() const {
4962
pthread_t current_thread = pthread_self();
5063
bool is_creation_thread_current = !!pthread_equal(current_thread, self_);
64+
if (disable_next_failure_ && !is_creation_thread_current) {
65+
disable_next_failure_ = false;
66+
return true;
67+
}
5168
#ifdef __APPLE__
5269
// TODO(https://github.com/flutter/flutter/issues/45272): Implement for
5370
// other platforms.

shell/common/shell.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,24 @@ void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) {
996996
void Shell::OnPlatformViewDispatchPlatformMessage(
997997
std::unique_ptr<PlatformMessage> message) {
998998
FML_DCHECK(is_setup_);
999-
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
999+
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
1000+
if (!task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()) {
1001+
std::scoped_lock lock(misbehaving_message_channels_mutex_);
1002+
auto inserted = misbehaving_message_channels_.insert(message->channel());
1003+
if (inserted.second) {
1004+
FML_LOG(ERROR)
1005+
<< "The '" << message->channel()
1006+
<< "' channel sent a message from native to Flutter on a "
1007+
"non-platform thread. Platform channel messages must be sent on "
1008+
"the platform thread. Failure to do so may result in data loss or "
1009+
"crashes, and must be fixed in the plugin or application code "
1010+
"creating that channel.\n"
1011+
"See https://docs.flutter.dev/platform-integration/"
1012+
"platform-channels#channels-and-platform-threading for more "
1013+
"information.";
1014+
}
1015+
}
1016+
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
10001017

10011018
// The static leak checker gets confused by the use of fml::MakeCopyable.
10021019
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)

shell/common/shell.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,12 @@ class Shell final : public PlatformView::Delegate,
412412
std::function<bool(const ServiceProtocol::Handler::ServiceProtocolMap&,
413413
rapidjson::Document*)>;
414414

415+
/// A collection of message channels (by name) that have sent at least one
416+
/// message from a non-platform thread. Used to prevent printing the error log
417+
/// more than once per channel, as a badly behaving plugin may send multiple
418+
/// messages per second indefinitely.
419+
std::mutex misbehaving_message_channels_mutex_;
420+
std::set<std::string> misbehaving_message_channels_;
415421
const TaskRunners task_runners_;
416422
const fml::RefPtr<fml::RasterThreadMerger> parent_raster_thread_merger_;
417423
std::shared_ptr<ResourceCacheLimitCalculator>

shell/common/shell_test.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ ShellTest::ShellTest()
2525
ThreadHost::Type::Platform | ThreadHost::Type::IO |
2626
ThreadHost::Type::UI | ThreadHost::Type::RASTER) {}
2727

28+
void ShellTest::SendPlatformMessage(Shell* shell,
29+
std::unique_ptr<PlatformMessage> message) {
30+
shell->OnPlatformViewDispatchPlatformMessage(std::move(message));
31+
}
32+
2833
void ShellTest::SendEnginePlatformMessage(
2934
Shell* shell,
3035
std::unique_ptr<PlatformMessage> message) {

shell/common/shell_test.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ class ShellTest : public FixtureTest {
5656

5757
fml::TimePoint GetLatestFrameTargetTime(Shell* shell) const;
5858

59+
void SendPlatformMessage(Shell* shell,
60+
std::unique_ptr<PlatformMessage> message);
61+
5962
void SendEnginePlatformMessage(Shell* shell,
6063
std::unique_ptr<PlatformMessage> message);
6164

0 commit comments

Comments
 (0)