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

Commit 8def204

Browse files
committed
Made responses to platform methods threadsafe in linux
1 parent 35ecb2b commit 8def204

File tree

3 files changed

+109
-32
lines changed

3 files changed

+109
-32
lines changed

shell/platform/linux/fl_binary_messenger.cc

Lines changed: 32 additions & 32 deletions
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

@@ -141,19 +143,6 @@ static void platform_message_handler_free(gpointer data) {
141143
g_free(self);
142144
}
143145

144-
static void engine_weak_notify_cb(gpointer user_data,
145-
GObject* where_the_object_was) {
146-
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(user_data);
147-
self->engine = nullptr;
148-
149-
// Disconnect any handlers.
150-
// Take the reference in case a handler tries to modify this table.
151-
g_autoptr(GHashTable) handlers = self->platform_message_handlers;
152-
self->platform_message_handlers = g_hash_table_new_full(
153-
g_str_hash, g_str_equal, g_free, platform_message_handler_free);
154-
g_hash_table_remove_all(handlers);
155-
}
156-
157146
static gboolean fl_binary_messenger_platform_message_cb(
158147
FlEngine* engine,
159148
const gchar* channel,
@@ -179,11 +168,7 @@ static gboolean fl_binary_messenger_platform_message_cb(
179168

180169
static void fl_binary_messenger_impl_dispose(GObject* object) {
181170
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(object);
182-
183-
if (self->engine != nullptr) {
184-
g_object_weak_unref(G_OBJECT(self->engine), engine_weak_notify_cb, self);
185-
self->engine = nullptr;
186-
}
171+
g_weak_ref_clear(&self->engine);
187172

188173
g_clear_pointer(&self->platform_message_handlers, g_hash_table_unref);
189174

@@ -199,7 +184,8 @@ static void set_message_handler_on_channel(
199184
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
200185

201186
// Don't set handlers if engine already gone.
202-
if (self->engine == nullptr) {
187+
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
188+
if (engine == nullptr) {
203189
if (handler != nullptr) {
204190
g_warning(
205191
"Attempted to set message handler on an FlBinaryMessenger without an "
@@ -220,6 +206,12 @@ static void set_message_handler_on_channel(
220206
}
221207
}
222208

209+
gboolean do_unref(gpointer value) {
210+
g_object_unref(value);
211+
return G_SOURCE_REMOVE;
212+
}
213+
214+
// Note: This function can be called from any thread.
223215
static gboolean send_response(FlBinaryMessenger* messenger,
224216
FlBinaryMessengerResponseHandle* response_handle_,
225217
GBytes* response,
@@ -233,21 +225,27 @@ static gboolean send_response(FlBinaryMessenger* messenger,
233225
g_return_val_if_fail(response_handle->messenger == self, FALSE);
234226
g_return_val_if_fail(response_handle->response_handle != nullptr, FALSE);
235227

236-
if (self->engine == nullptr) {
228+
FlEngine* engine = FL_ENGINE(g_weak_ref_get(&self->engine));
229+
if (engine == nullptr) {
237230
return TRUE;
238231
}
239232

233+
gboolean result = false;
240234
if (response_handle->response_handle == nullptr) {
241235
g_set_error(
242236
error, FL_BINARY_MESSENGER_ERROR,
243237
FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED,
244238
"Attempted to respond to a message that is already responded to");
245-
return FALSE;
239+
result = FALSE;
240+
} else {
241+
result = fl_engine_send_platform_message_response(
242+
engine, response_handle->response_handle, response, error);
243+
response_handle->response_handle = nullptr;
246244
}
247245

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

252250
return result;
253251
}
@@ -267,12 +265,13 @@ static void send_on_channel(FlBinaryMessenger* messenger,
267265
gpointer user_data) {
268266
FlBinaryMessengerImpl* self = FL_BINARY_MESSENGER_IMPL(messenger);
269267

270-
if (self->engine == nullptr) {
268+
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
269+
if (engine == nullptr) {
271270
return;
272271
}
273272

274273
fl_engine_send_platform_message(
275-
self->engine, channel, message, cancellable,
274+
engine, channel, message, cancellable,
276275
callback != nullptr ? platform_message_ready_cb : nullptr,
277276
callback != nullptr ? g_task_new(self, cancellable, callback, user_data)
278277
: nullptr);
@@ -287,11 +286,12 @@ static GBytes* send_on_channel_finish(FlBinaryMessenger* messenger,
287286
g_autoptr(GTask) task = G_TASK(result);
288287
GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr));
289288

290-
if (self->engine == nullptr) {
289+
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
290+
if (engine == nullptr) {
291291
return nullptr;
292292
}
293293

294-
return fl_engine_send_platform_message_finish(self->engine, r, error);
294+
return fl_engine_send_platform_message_finish(engine, r, error);
295295
}
296296

297297
static void fl_binary_messenger_impl_class_init(
@@ -321,8 +321,7 @@ FlBinaryMessenger* fl_binary_messenger_new(FlEngine* engine) {
321321
// Added to stop compiler complaining about an unused function.
322322
FL_IS_BINARY_MESSENGER_IMPL(self);
323323

324-
self->engine = engine;
325-
g_object_weak_ref(G_OBJECT(engine), engine_weak_notify_cb, self);
324+
g_weak_ref_init(&self->engine, G_OBJECT(engine));
326325

327326
fl_engine_set_platform_message_handler(
328327
engine, fl_binary_messenger_platform_message_cb, self, NULL);
@@ -343,6 +342,7 @@ G_MODULE_EXPORT void fl_binary_messenger_set_message_handler_on_channel(
343342
self, channel, handler, user_data, destroy_notify);
344343
}
345344

345+
// Note: This function can be called from any thread.
346346
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(
347347
FlBinaryMessenger* self,
348348
FlBinaryMessengerResponseHandle* response_handle,

shell/platform/linux/fl_binary_messenger_test.cc

Lines changed: 76 additions & 0 deletions
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,78 @@ 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+
}

shell/platform/linux/fl_engine.cc

Lines changed: 1 addition & 0 deletions
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,

0 commit comments

Comments
 (0)