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: <https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/316>
This commit is contained in:
Peter Hutterer 2024-12-04 14:50:57 +10:00 committed by Marge Bot
parent 95eb3c4bb3
commit 2cc5e56e28
4 changed files with 95 additions and 28 deletions

View file

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

View file

@ -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),

View file

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

View file

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