From 2cc5e56e2848e8cc5229c28bb23c134d0581d1fb Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 4 Dec 2024 14:50:57 +1000 Subject: [PATCH] eis: revamp the internal sync callback Identical to "ei: revamp the internal sync callback" but for libeis. In prep work for exposing some of this to the caller, this adds a new object that carries the our callbacks including the user data (if any). This is an internal system only and is only used in the handshake implementation where we don't have userdata anyway. The new approach is: the callback has an object with a done() and destroy() callback and the user data, done() is called when we receive the message from the protocol, destroy() on destroy regardless whether we got done() first. This allows a caller to clean up user data even where the callback was not triggered because we got disconnected first. Part-of: --- src/libei-connection.h | 3 +- src/libeis-connection.c | 70 +++++++++++++++++++++++++++++------------ src/libeis-connection.h | 40 +++++++++++++++++++++-- src/libeis-handshake.c | 10 ++++-- 4 files changed, 95 insertions(+), 28 deletions(-) diff --git a/src/libei-connection.h b/src/libei-connection.h index 6b1888a..12745f0 100644 --- a/src/libei-connection.h +++ b/src/libei-connection.h @@ -80,11 +80,10 @@ ei_connection_sync_callback_new(struct ei *ei, ei_connection_sync_callback_done_t done, ei_connection_sync_callback_destroy_t destroy, void *user_data); -struct ei * -ei_connection_sync_callback_get_context(struct ei_connection_sync_callback *callback); OBJECT_DECLARE_REF(ei_connection_sync_callback); OBJECT_DECLARE_UNREF(ei_connection_sync_callback); +OBJECT_DECLARE_GETTER(ei_connection_sync_callback, context, struct ei*); OBJECT_DECLARE_GETTER(ei_connection_sync_callback, user_data, void*); DEFINE_UNREF_CLEANUP_FUNC(ei_connection_sync_callback); diff --git a/src/libeis-connection.c b/src/libeis-connection.c index f470578..0226856 100644 --- a/src/libeis-connection.c +++ b/src/libeis-connection.c @@ -46,7 +46,7 @@ eis_connection_destroy(struct eis_connection *connection) struct eis_pingpong *cb; list_for_each_safe(cb, &connection->pending_pingpongs, link) { list_remove(&cb->link); - free(eis_pingpong_get_user_data(cb)); + eis_connection_ping_callback_unref(cb->user_data); eis_pingpong_unref(cb); } } @@ -108,20 +108,53 @@ eis_connection_new(struct eis_client *client) return connection; /* ref owned by caller */ } -struct pingpong_user_data { - eis_connection_ping_callback_t cb; - void *user_data; -}; +static void +eis_connection_ping_callback_destroy(struct eis_connection_ping_callback *callback) +{ + if (callback->destroy) + callback->destroy(callback); +} + +static +OBJECT_IMPLEMENT_CREATE(eis_connection_ping_callback); +OBJECT_IMPLEMENT_REF(eis_connection_ping_callback); +OBJECT_IMPLEMENT_UNREF(eis_connection_ping_callback); +OBJECT_IMPLEMENT_GETTER(eis_connection_ping_callback, user_data, void *); +static +OBJECT_IMPLEMENT_PARENT(eis_connection_ping_callback, eis_connection); + +struct eis_connection * +eis_connection_ping_callback_get_connection(struct eis_connection_ping_callback *callback) +{ + return eis_connection_ping_callback_parent(callback); +} + +struct eis_client * +eis_connection_ping_callback_get_client(struct eis_connection_ping_callback *callback) +{ + struct eis_connection *connection = eis_connection_ping_callback_get_connection(callback); + return eis_connection_get_client(connection); +} + +struct eis_connection_ping_callback * +eis_connection_ping_callback_new(struct eis_connection *connection, + eis_connection_ping_callback_done_t done, + eis_connection_ping_callback_destroy_t destroy, + void *user_data) +{ + struct eis_connection_ping_callback *callback = eis_connection_ping_callback_create(&connection->object); + callback->done = done; + callback->destroy = destroy; + callback->user_data = user_data; + return callback; +} static void -ping_pingpong(struct eis_pingpong *pingpong, void *pingpong_data, - uint64_t proto_data) +pingpong_callback(struct eis_pingpong *pingpong, void *pingpong_data, uint64_t proto_data) { - struct eis_connection *connection = pingpong_data; - - _cleanup_free_ struct pingpong_user_data *data = eis_pingpong_get_user_data(pingpong); - if (data->cb) - data->cb(connection, data->user_data); + _unref_(eis_connection_ping_callback) *data = eis_pingpong_get_user_data(pingpong); + if (data->done) + data->done(data); /* remove from pending callbacks */ list_remove(&pingpong->link); @@ -129,21 +162,18 @@ ping_pingpong(struct eis_pingpong *pingpong, void *pingpong_data, } void -eis_connection_ping(struct eis_connection *connection, eis_connection_ping_callback_t cb, - void *user_data) +eis_connection_ping(struct eis_connection *connection, + struct eis_connection_ping_callback *cb) { struct eis_client *client = eis_connection_get_client(connection); /* This is double-wrapped because we only use this for debugging purposes for - * now. The actual callback calls sync_callback with our connection, + * now. The actual callback calls pingpong_callback with our connection, * then we extract the user_data on the object and call into the * cb supplied to this function. */ - struct eis_pingpong *pingpong = eis_pingpong_new(client, ping_pingpong, connection); - struct pingpong_user_data *data = xalloc(sizeof *data); - data->cb = cb; - data->user_data = user_data; - eis_pingpong_set_user_data(pingpong, data); + struct eis_pingpong *pingpong = eis_pingpong_new(client, pingpong_callback, connection); + eis_pingpong_set_user_data(pingpong, eis_connection_ping_callback_ref(cb)); list_append(&connection->pending_pingpongs, &pingpong->link); eis_connection_event_ping(connection, eis_pingpong_get_id(pingpong), diff --git a/src/libeis-connection.h b/src/libeis-connection.h index f47ad5b..f4223c2 100644 --- a/src/libeis-connection.h +++ b/src/libeis-connection.h @@ -25,11 +25,13 @@ #pragma once +#include "util-mem.h" #include "util-object.h" #include "brei-shared.h" struct eis; struct eis_client; +struct eis_connection_ping_callback; /* This is a protocol-only object, not exposed in the API */ struct eis_connection { @@ -59,7 +61,39 @@ eis_connection_new(struct eis_client *client); typedef void (*eis_connection_ping_callback_t)(struct eis_connection *connection, void *user_data); +/** + * Called when the ei_callback.done event is received after + * an eis_connection_ping() request. + */ +typedef void (*eis_connection_ping_callback_done_t)(struct eis_connection_ping_callback *callback); + +/** + * Called for each registered callback when the last reference to it is + * destroyed. This should be used to clean up user_data, if need be. + * + * This function is always called, even if disconnected and the done() function is never called. + */ +typedef void (*eis_connection_ping_callback_destroy_t)(struct eis_connection_ping_callback *callback); + +struct eis_connection_ping_callback { + struct object object; /* parent is struct eis */ + eis_connection_ping_callback_done_t done; + eis_connection_ping_callback_destroy_t destroy; + void *user_data; +}; + +struct eis_connection_ping_callback * +eis_connection_ping_callback_new(struct eis_connection *connection, + eis_connection_ping_callback_done_t done, + eis_connection_ping_callback_destroy_t destroy, + void *user_data); + +OBJECT_DECLARE_REF(eis_connection_ping_callback); +OBJECT_DECLARE_UNREF(eis_connection_ping_callback); +OBJECT_DECLARE_GETTER(eis_connection_ping_callback, connection, struct eis_connection *); +OBJECT_DECLARE_GETTER(eis_connection_ping_callback, client, struct eis_client *); +OBJECT_DECLARE_GETTER(eis_connection_ping_callback, user_data, void*); +DEFINE_UNREF_CLEANUP_FUNC(eis_connection_ping_callback); + void -eis_connection_ping(struct eis_connection *connection, - eis_connection_ping_callback_t callback, - void *user_data); +eis_connection_ping(struct eis_connection *connection, struct eis_connection_ping_callback *callback); diff --git a/src/libeis-handshake.c b/src/libeis-handshake.c index 90a4de5..dab5f42 100644 --- a/src/libeis-handshake.c +++ b/src/libeis-handshake.c @@ -76,9 +76,9 @@ eis_handshake_get_id(struct eis_handshake *setup) } static void -pong(struct eis_connection *connection, void *user_data) +on_pong(struct eis_connection_ping_callback *callback) { - struct eis_client *client = eis_connection_get_client(connection); + struct eis_client *client = eis_connection_ping_callback_get_client(callback); eis_queue_connect_event(client); } @@ -152,9 +152,13 @@ client_msg_finish(struct eis_handshake *setup) setup->client_versions.ei_device == 0) { eis_client_disconnect(client); } else { + _unref_(eis_connection_ping_callback) *cb = eis_connection_ping_callback_new(client->connection, + on_pong, + NULL, + NULL); /* Force a ping/pong. This isn't necessary but it doesn't hurt much here * and it ensures that any client implementation doesn't have that part missing */ - eis_connection_ping(client->connection, pong, NULL); + eis_connection_ping(client->connection, cb); } client->setup = eis_handshake_unref(setup);