Make FlBasicMessageChannelResponseHandle a GObject to detect unresponded and double responded messages. (flutter/engine#18514)

This commit is contained in:
Robert Ancell 2020-05-21 11:15:56 +12:00 committed by GitHub
parent df5615108f
commit 7b4840a4fc
4 changed files with 160 additions and 70 deletions

View File

@ -23,33 +23,51 @@ struct _FlBasicMessageChannel {
gpointer message_handler_data;
};
// Wrap the binary messenger handle for type safety and to make the API
// consistent
struct _FlBasicMessageChannelResponseHandle {
GObject parent_instance;
FlBinaryMessengerResponseHandle* response_handle;
};
static FlBasicMessageChannelResponseHandle* response_handle_new(
FlBinaryMessengerResponseHandle* response_handle) {
FlBasicMessageChannelResponseHandle* handle =
static_cast<FlBasicMessageChannelResponseHandle*>(
g_malloc0(sizeof(FlBasicMessageChannelResponseHandle)));
handle->response_handle = response_handle;
return handle;
}
static void response_handle_free(FlBasicMessageChannelResponseHandle* handle) {
g_free(handle);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlBasicMessageChannelResponseHandle,
response_handle_free);
// Added here to stop the compiler from optimising this function away
G_MODULE_EXPORT GType fl_basic_message_channel_get_type();
G_DEFINE_TYPE(FlBasicMessageChannel, fl_basic_message_channel, G_TYPE_OBJECT)
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle,
fl_basic_message_channel_response_handle,
G_TYPE_OBJECT)
static void fl_basic_message_channel_response_handle_dispose(GObject* object) {
FlBasicMessageChannelResponseHandle* self =
FL_BASIC_MESSAGE_CHANNEL_RESPONSE_HANDLE(object);
g_clear_object(&self->response_handle);
G_OBJECT_CLASS(fl_basic_message_channel_response_handle_parent_class)
->dispose(object);
}
static void fl_basic_message_channel_response_handle_class_init(
FlBasicMessageChannelResponseHandleClass* klass) {
G_OBJECT_CLASS(klass)->dispose =
fl_basic_message_channel_response_handle_dispose;
}
static void fl_basic_message_channel_response_handle_init(
FlBasicMessageChannelResponseHandle* self) {}
static FlBasicMessageChannelResponseHandle*
fl_basic_message_channel_response_handle_new(
FlBinaryMessengerResponseHandle* response_handle) {
FlBasicMessageChannelResponseHandle* self =
FL_BASIC_MESSAGE_CHANNEL_RESPONSE_HANDLE(g_object_new(
fl_basic_message_channel_response_handle_get_type(), nullptr));
self->response_handle =
FL_BINARY_MESSENGER_RESPONSE_HANDLE(g_object_ref(response_handle));
return self;
}
// Called when a binary message is received on this channel
static void message_cb(FlBinaryMessenger* messenger,
@ -74,8 +92,9 @@ static void message_cb(FlBinaryMessenger* messenger,
nullptr);
}
self->message_handler(self, message_value,
response_handle_new(response_handle),
g_autoptr(FlBasicMessageChannelResponseHandle) handle =
fl_basic_message_channel_response_handle_new(response_handle);
self->message_handler(self, message_value, handle,
self->message_handler_data);
}
@ -146,18 +165,18 @@ G_MODULE_EXPORT gboolean fl_basic_message_channel_respond(
GError** error) {
g_return_val_if_fail(FL_IS_BASIC_MESSAGE_CHANNEL(self), FALSE);
g_return_val_if_fail(response_handle != nullptr, FALSE);
// Take reference to ensure it is freed
g_autoptr(FlBasicMessageChannelResponseHandle) owned_response_handle =
response_handle;
g_return_val_if_fail(response_handle->response_handle != nullptr, FALSE);
g_autoptr(GBytes) data =
fl_message_codec_encode_message(self->codec, message, error);
if (data == nullptr)
return FALSE;
return fl_binary_messenger_send_response(
self->messenger, owned_response_handle->response_handle, data, error);
gboolean result = fl_binary_messenger_send_response(
self->messenger, response_handle->response_handle, data, error);
g_clear_object(&response_handle->response_handle);
return result;
}
G_MODULE_EXPORT void fl_basic_message_channel_send(FlBasicMessageChannel* self,

View File

@ -10,6 +10,9 @@
#include <gmodule.h>
G_DEFINE_QUARK(fl_binary_messenger_codec_error_quark,
fl_binary_messenger_codec_error)
struct _FlBinaryMessenger {
GObject parent_instance;
@ -21,12 +24,61 @@ struct _FlBinaryMessenger {
G_DEFINE_TYPE(FlBinaryMessenger, fl_binary_messenger, G_TYPE_OBJECT)
struct _FlBinaryMessengerResponseHandle {
GObject parent_instance;
// Messenger sending response on
FlBinaryMessenger* messenger;
// Handle to send the response with. This is cleared to nullptr when it is
// used.
const FlutterPlatformMessageResponseHandle* response_handle;
};
G_DEFINE_TYPE(FlBinaryMessengerResponseHandle,
fl_binary_messenger_response_handle,
G_TYPE_OBJECT)
static void fl_binary_messenger_response_handle_dispose(GObject* object) {
FlBinaryMessengerResponseHandle* self =
FL_BINARY_MESSENGER_RESPONSE_HANDLE(object);
if (self->response_handle != nullptr && self->messenger->engine != nullptr)
g_critical("FlBinaryMessengerResponseHandle was not responded to");
g_clear_object(&self->messenger);
self->response_handle = nullptr;
G_OBJECT_CLASS(fl_binary_messenger_response_handle_parent_class)
->dispose(object);
}
static void fl_binary_messenger_response_handle_class_init(
FlBinaryMessengerResponseHandleClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_binary_messenger_response_handle_dispose;
}
static void fl_binary_messenger_response_handle_init(
FlBinaryMessengerResponseHandle* self) {}
static FlBinaryMessengerResponseHandle* fl_binary_messenger_response_handle_new(
FlBinaryMessenger* messenger,
const FlutterPlatformMessageResponseHandle* response_handle) {
FlBinaryMessengerResponseHandle* self = FL_BINARY_MESSENGER_RESPONSE_HANDLE(
g_object_new(fl_binary_messenger_response_handle_get_type(), nullptr));
self->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger));
self->response_handle = response_handle;
return self;
}
typedef struct {
FlBinaryMessengerMessageHandler message_handler;
gpointer message_handler_data;
} PlatformMessageHandler;
PlatformMessageHandler* platform_message_handler_new(
static PlatformMessageHandler* platform_message_handler_new(
FlBinaryMessengerMessageHandler handler,
gpointer user_data) {
PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(
@ -36,32 +88,11 @@ PlatformMessageHandler* platform_message_handler_new(
return self;
}
void platform_message_handler_free(gpointer data) {
static void platform_message_handler_free(gpointer data) {
PlatformMessageHandler* self = static_cast<PlatformMessageHandler*>(data);
g_free(self);
}
struct _FlBinaryMessengerResponseHandle {
const FlutterPlatformMessageResponseHandle* response_handle;
};
static FlBinaryMessengerResponseHandle* response_handle_new(
const FlutterPlatformMessageResponseHandle* response_handle) {
FlBinaryMessengerResponseHandle* self =
static_cast<FlBinaryMessengerResponseHandle*>(
g_malloc0(sizeof(FlBinaryMessengerResponseHandle)));
self->response_handle = response_handle;
return self;
}
static void response_handle_free(FlBinaryMessengerResponseHandle* self) {
g_free(self);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FlBinaryMessengerResponseHandle,
response_handle_free);
static void engine_weak_notify_cb(gpointer user_data, GObject* object) {
FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data);
self->engine = nullptr;
@ -75,14 +106,13 @@ static gboolean fl_binary_messenger_platform_message_cb(
void* user_data) {
FlBinaryMessenger* self = FL_BINARY_MESSENGER(user_data);
FlBinaryMessengerResponseHandle* handle =
response_handle_new(response_handle);
PlatformMessageHandler* handler = static_cast<PlatformMessageHandler*>(
g_hash_table_lookup(self->platform_message_handlers, channel));
if (handler == nullptr)
return FALSE;
g_autoptr(FlBinaryMessengerResponseHandle) handle =
fl_binary_messenger_response_handle_new(self, response_handle);
handler->message_handler(self, channel, message, handle,
handler->message_handler_data);
@ -148,16 +178,23 @@ G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(
GError** error) {
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(self), FALSE);
g_return_val_if_fail(response_handle != nullptr, FALSE);
// Take reference to ensure it is freed
g_autoptr(FlBinaryMessengerResponseHandle) owned_response_handle =
response_handle;
g_return_val_if_fail(response_handle->messenger == self, FALSE);
g_return_val_if_fail(response_handle->response_handle != nullptr, FALSE);
if (self->engine == nullptr)
return TRUE;
if (response_handle->response_handle == nullptr) {
g_set_error(
error, FL_BINARY_MESSENGER_ERROR,
FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED,
"Attempted to respond to a message that is already responded to");
return FALSE;
}
gboolean result = fl_engine_send_platform_message_response(
self->engine, owned_response_handle->response_handle, response, error);
self->engine, response_handle->response_handle, response, error);
response_handle->response_handle = nullptr;
return result;
}

View File

@ -23,6 +23,12 @@ G_DECLARE_FINAL_TYPE(FlBasicMessageChannel,
BASIC_MESSAGE_CHANNEL,
GObject)
G_DECLARE_FINAL_TYPE(FlBasicMessageChannelResponseHandle,
fl_basic_message_channel_response_handle,
FL,
BASIC_MESSAGE_CHANNEL_RESPONSE_HANDLE,
GObject)
/**
* FlBasicMessageChannel:
*
@ -79,19 +85,23 @@ G_DECLARE_FINAL_TYPE(FlBasicMessageChannel,
/**
* FlBasicMessageChannelResponseHandle:
*
* A handle used to respond to messages.
* #FlBasicMessageChannelResponseHandle is an object used to send responses
* with.
*/
typedef struct _FlBasicMessageChannelResponseHandle
FlBasicMessageChannelResponseHandle;
/**
* FlBasicMessageChannelMessageHandler:
* @channel: an #FlBasicMessageChannel.
* @message: message received.
* @response_handle: (transfer full): a handle to respond to the message with.
* @response_handle: a handle to respond to the message with.
* @user_data: (closure): data provided when registering this handler.
*
* Function called when a message is received.
* Function called when a message is received. Call
* fl_basic_message_channel_respond() to respond to this message. If the
* response is not occurring in this callback take a reference to
* @response_handle and release that once it has been responded to. Failing to
* respond before the last reference to @response_handle is dropped is a
* programming error.
*/
typedef void (*FlBasicMessageChannelMessageHandler)(
FlBasicMessageChannel* channel,
@ -132,7 +142,7 @@ void fl_basic_message_channel_set_message_handler(
/**
* fl_basic_message_channel_respond:
* @channel: an #FlBasicMessageChannel.
* @response_handle: (transfer full): handle that was provided in a
* @response_handle: handle that was provided in a
* #FlBasicMessageChannelMessageHandler.
* @message: (allow-none): message response to send or %NULL for an empty
* response.

View File

@ -14,12 +14,33 @@
G_BEGIN_DECLS
/**
* FlBinaryMessengerError:
* @FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED: unable to send response, this
* message has already been responded to.
*
* Errors for #FlBinaryMessenger objects to set on failures.
*/
#define FL_BINARY_MESSENGER_ERROR fl_binary_messenger_codec_error_quark()
typedef enum {
FL_BINARY_MESSENGER_ERROR_ALREADY_RESPONDED,
} FlBinaryMessengerError;
GQuark fl_binary_messenger_codec_error_quark(void) G_GNUC_CONST;
G_DECLARE_FINAL_TYPE(FlBinaryMessenger,
fl_binary_messenger,
FL,
BINARY_MESSENGER,
GObject)
G_DECLARE_FINAL_TYPE(FlBinaryMessengerResponseHandle,
fl_binary_messenger_response_handle,
FL,
BINARY_MESSENGER_RESPONSE_HANDLE,
GObject)
/**
* FlBinaryMessenger:
*
@ -30,20 +51,23 @@ G_DECLARE_FINAL_TYPE(FlBinaryMessenger,
/**
* FlBinaryMessengerResponseHandle:
*
* A handle used to respond to platform messages.
* #FlBinaryMessengerResponseHandle is an object used to send responses with.
*/
typedef struct _FlBinaryMessengerResponseHandle FlBinaryMessengerResponseHandle;
/**
* FlBinaryMessengerMessageHandler:
* @messenger: an #FlBinaryMessenger.
* @channel: channel message received on.
* @message: message content received from Dart.
* @response_handle: (transfer full): a handle to respond to the message with.
* @response_handle: a handle to respond to the message with.
* @user_data: (closure): data provided when registering this handler.
*
* Function called when platform messages are received. The receiver must
* call fl_binary_messenger_send_response() to avoid leaking the handle.
* Function called when platform messages are received. Call
* fl_binary_messenger_send_response() to respond to this message. If the
* response is not occurring in this callback take a reference to
* @response_handle and release that once it has been responded to. Failing to
* respond before the last reference to @response_handle is dropped is a
* programming error.
*/
typedef void (*FlBinaryMessengerMessageHandler)(
FlBinaryMessenger* messenger,
@ -74,7 +98,7 @@ void fl_binary_messenger_set_message_handler_on_channel(
/**
* fl_binary_messenger_send_response:
* @binary_messenger: an #FlBinaryMessenger.
* @response_handle: (transfer full): handle that was provided in a
* @response_handle: handle that was provided in a
* #FlBinaryMessengerMessageHandler.
* @response: (allow-none): response to send or %NULL for an empty response.
* @error: (allow-none): #GError location to store the error occurring, or %NULL