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);