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

Commit 9463b5d

Browse files
authored
Made responses to platform methods threadsafe in linux (#37689)
* Made responses to platform methods threadsafe in linux * brought back the logic that the engine clears handlers * removed leak * more feedback
1 parent 1603fa1 commit 9463b5d

File tree

4 files changed

+135
-18
lines changed

4 files changed

+135
-18
lines changed

shell/platform/linux/fl_binary_messenger.cc

+38-17
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ G_DEFINE_INTERFACE(FlBinaryMessenger, fl_binary_messenger, G_TYPE_OBJECT)
3333
struct _FlBinaryMessengerImpl {
3434
GObject parent_instance;
3535

36-
FlEngine* engine;
36+
GWeakRef engine;
3737

3838
// PlatformMessageHandler keyed by channel name.
3939
GHashTable* platform_message_handlers;
@@ -81,7 +81,9 @@ static void fl_binary_messenger_response_handle_impl_dispose(GObject* object) {
8181
FlBinaryMessengerResponseHandleImpl* self =
8282
FL_BINARY_MESSENGER_RESPONSE_HANDLE_IMPL(object);
8383

84-
if (self->response_handle != nullptr && self->messenger->engine != nullptr) {
84+
g_autoptr(FlEngine) engine =
85+
FL_ENGINE(g_weak_ref_get(&self->messenger->engine));
86+
if (self->response_handle != nullptr && engine != nullptr) {
8587
g_critical("FlBinaryMessengerResponseHandle was not responded to");
8688
}
8789

@@ -144,7 +146,6 @@ static void platform_message_handler_free(gpointer data) {
144146
static void engine_weak_notify_cb(gpointer user_data,
145147
GObject* where_the_object_was) {
146148
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(user_data);
147-
self->engine = nullptr;
148149

149150
// Disconnect any handlers.
150151
// Take the reference in case a handler tries to modify this table.
@@ -180,11 +181,15 @@ static gboolean fl_binary_messenger_platform_message_cb(
180181
static void fl_binary_messenger_impl_dispose(GObject* object) {
181182
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(object);
182183

183-
if (self->engine != nullptr) {
184-
g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self);
185-
self->engine = nullptr;
184+
{
185+
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
186+
if (engine) {
187+
g_object_weak_unref(G_OBJECT(engine), engine_weak_notify_cb, self);
188+
}
186189
}
187190

191+
g_weak_ref_clear(&self->engine);
192+
188193
g_clear_pointer(&self->platform_message_handlers, g_hash_table_unref);
189194

190195
G_OBJECT_CLASS(fl_binary_messenger_impl_parent_class)->dispose(object);
@@ -199,7 +204,8 @@ static void set_message_handler_on_channel(
199204
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
200205

201206
// Don't set handlers if engine already gone.
202-
if (self->engine == nullptr) {
207+
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
208+
if (engine == nullptr) {
203209
if (handler != nullptr) {
204210
g_warning(
205211
"Attempted to set message handler on an FlBinaryMessenger without an "
@@ -220,6 +226,12 @@ static void set_message_handler_on_channel(
220226
}
221227
}
222228

229+
static gboolean do_unref(gpointer value) {
230+
g_object_unref(value);
231+
return G_SOURCE_REMOVE;
232+
}
233+
234+
// Note: This function can be called from any thread.
223235
static gboolean send_response(FlBinaryMessenger* messenger,
224236
FlBinaryMessengerResponseHandle* response_handle_,
225237
GBytes* response,
@@ -233,21 +245,27 @@ static gboolean send_response(FlBinaryMessenger* messenger,
233245
g_return_val_if_fail(response_handle->messenger == self, FALSE);
234246
g_return_val_if_fail(response_handle->response_handle != nullptr, FALSE);
235247

236-
if (self->engine == nullptr) {
248+
FlEngine* engine = FL_ENGINE(g_weak_ref_get(&self->engine));
249+
if (engine == nullptr) {
237250
return TRUE;
238251
}
239252

253+
gboolean result = false;
240254
if (response_handle->response_handle == nullptr) {
241255
g_set_error(
242256
error, FL_BINARY_MESSENGER_ERROR,
243257
FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED,
244258
"Attempted to respond to a message that is already responded to");
245-
return FALSE;
259+
result = FALSE;
260+
} else {
261+
result = fl_engine_send_platform_message_response(
262+
engine, response_handle->response_handle, response, error);
263+
response_handle->response_handle = nullptr;
246264
}
247265

248-
gboolean result = fl_engine_send_platform_message_response(
249-
self->engine, response_handle->response_handle, response, error);
250-
response_handle->response_handle = nullptr;
266+
// This guarantees that the dispose method for the engine is executed
267+
// on the platform thread in the rare chance this is the last ref.
268+
g_idle_add(do_unref, engine);
251269

252270
return result;
253271
}
@@ -267,12 +285,13 @@ static void send_on_channel(FlBinaryMessenger* messenger,
267285
gpointer user_data) {
268286
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
269287

270-
if (self->engine == nullptr) {
288+
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
289+
if (engine == nullptr) {
271290
return;
272291
}
273292

274293
fl_engine_send_platform_message(
275-
self->engine, channel, message, cancellable,
294+
engine, channel, message, cancellable,
276295
callback != nullptr ? platform_message_ready_cb : nullptr,
277296
callback != nullptr ? g_task_new(self, cancellable, callback, user_data)
278297
: nullptr);
@@ -287,11 +306,12 @@ static GBytes* send_on_channel_finish(FlBinaryMessenger* messenger,
287306
g_autoptr(GTask) task = G_TASK(result);
288307
GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr));
289308

290-
if (self->engine == nullptr) {
309+
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
310+
if (engine == nullptr) {
291311
return nullptr;
292312
}
293313

294-
return fl_engine_send_platform_message_finish(self->engine, r, error);
314+
return fl_engine_send_platform_message_finish(engine, r, error);
295315
}
296316

297317
static void fl_binary_messenger_impl_class_init(
@@ -321,7 +341,7 @@ FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine) {
321341
// Added to stop compiler complaining about an unused function.
322342
FL_IS_BINARY_MESSENGER_IMPL(self);
323343

324-
self->engine = engine;
344+
g_weak_ref_init(&self->engine, G_OBJECT(engine));
325345
g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self);
326346

327347
fl_engine_set_platform_message_handler(
@@ -343,6 +363,7 @@ G_MODULE_EXPORT void fl_binary_messenger_set_message_handler_on_channel(
343363
self, channel, handler, user_data, destroy_notify);
344364
}
345365

366+
// Note: This function can be called from any thread.
346367
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(
347368
FlBinaryMessenger* self,
348369
FlBinaryMessengerResponseHandle* response_handle,

shell/platform/linux/fl_binary_messenger_test.cc

+95
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Included first as it collides with the X11 headers.
66
#include "gtest/gtest.h"
77

8+
#include <pthread.h>
89
#include <cstring>
910

1011
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
@@ -384,3 +385,97 @@ TEST(FlBinaryMessengerTest, ReceiveMessage) {
384385
// Blocks here until response_cb is called.
385386
g_main_loop_run(loop);
386387
}
388+
389+
struct RespondsOnBackgroundThreadInfo {
390+
FlBinaryMessenger* messenger;
391+
FlBinaryMessengerResponseHandle* response_handle;
392+
GMainLoop* loop;
393+
};
394+
395+
static gboolean cleanup_responds_on_background_thread_info(gpointer user_data) {
396+
RespondsOnBackgroundThreadInfo* info =
397+
static_cast<RespondsOnBackgroundThreadInfo*>(user_data);
398+
GMainLoop* loop = info->loop;
399+
400+
g_object_unref(info->messenger);
401+
g_object_unref(info->response_handle);
402+
free(info);
403+
404+
g_main_loop_quit(static_cast<GMainLoop*>(loop));
405+
406+
return G_SOURCE_REMOVE;
407+
}
408+
409+
static void* response_from_thread_main(void* user_data) {
410+
RespondsOnBackgroundThreadInfo* info =
411+
static_cast<RespondsOnBackgroundThreadInfo*>(user_data);
412+
413+
fl_binary_messenger_send_response(info->messenger, info->response_handle,
414+
nullptr, nullptr);
415+
416+
g_idle_add(cleanup_responds_on_background_thread_info, info);
417+
418+
return nullptr;
419+
}
420+
421+
static void response_from_thread_cb(
422+
FlBinaryMessenger* messenger,
423+
const gchar* channel,
424+
GBytes* message,
425+
FlBinaryMessengerResponseHandle* response_handle,
426+
gpointer user_data) {
427+
EXPECT_NE(message, nullptr);
428+
pthread_t thread;
429+
RespondsOnBackgroundThreadInfo* info =
430+
static_cast<RespondsOnBackgroundThreadInfo*>(
431+
malloc(sizeof(RespondsOnBackgroundThreadInfo)));
432+
info->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger));
433+
info->response_handle =
434+
FL_BINARY_MESSENGER_RESPONSE_HANDLE(g_object_ref(response_handle));
435+
info->loop = static_cast<GMainLoop*>(user_data);
436+
EXPECT_EQ(0,
437+
pthread_create(&thread, nullptr, &response_from_thread_main, info));
438+
}
439+
440+
TEST(FlBinaryMessengerTest, RespondOnBackgroundThread) {
441+
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
442+
443+
g_autoptr(FlEngine) engine = make_mock_engine();
444+
FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
445+
446+
// Listen for messages from the engine.
447+
fl_binary_messenger_set_message_handler_on_channel(
448+
messenger, "test/messages", message_cb, nullptr, nullptr);
449+
450+
// Listen for response from the engine.
451+
fl_binary_messenger_set_message_handler_on_channel(
452+
messenger, "test/responses", response_from_thread_cb, loop, nullptr);
453+
454+
// Trigger the engine to send a message.
455+
const char* text = "Marco!";
456+
g_autoptr(GBytes) message = g_bytes_new(text, strlen(text));
457+
fl_binary_messenger_send_on_channel(messenger, "test/send-message", message,
458+
nullptr, nullptr, nullptr);
459+
460+
// Blocks here until response_cb is called.
461+
g_main_loop_run(loop);
462+
}
463+
464+
static void kill_handler_notify_cb(gpointer was_called) {
465+
*static_cast<gboolean*>(was_called) = TRUE;
466+
}
467+
468+
TEST(FlBinaryMessengerTest, DeletingEngineClearsHandlers) {
469+
FlEngine* engine = make_mock_engine();
470+
g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
471+
gboolean was_killed = FALSE;
472+
473+
// Listen for messages from the engine.
474+
fl_binary_messenger_set_message_handler_on_channel(messenger, "test/messages",
475+
message_cb, &was_killed,
476+
kill_handler_notify_cb);
477+
478+
g_clear_object(&engine);
479+
480+
ASSERT_TRUE(was_killed);
481+
}

shell/platform/linux/fl_engine.cc

+1
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@ void fl_engine_set_on_pre_engine_restart_handler(
616616
self->on_pre_engine_restart_handler_destroy_notify = destroy_notify;
617617
}
618618

619+
// Note: This function can be called from any thread.
619620
gboolean fl_engine_send_platform_message_response(
620621
FlEngine* self,
621622
const FlutterPlatformMessageResponseHandle* handle,

shell/platform/linux/public/flutter_linux/fl_binary_messenger.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ void fl_binary_messenger_set_message_handler_on_channel(
140140
* @error: (allow-none): #GError location to store the error occurring, or %NULL
141141
* to ignore.
142142
*
143-
* Responds to a platform message.
143+
* Responds to a platform message. This function is thread-safe.
144144
*
145145
* Returns: %TRUE on success.
146146
*/

0 commit comments

Comments
 (0)