libei: free all devices on context removal

This requires a new state so we can differ between having a connection and not
having one.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2020-08-07 11:28:39 +10:00
parent 11be8c7a2f
commit 1c5d6c1e2b
3 changed files with 71 additions and 36 deletions

View file

@ -111,8 +111,11 @@ ei_device_add(struct ei_device *device)
_public_ void
ei_device_remove(struct ei_device *device)
{
ei_remove_device(device);
if (device->state == EI_DEVICE_STATE_REMOVED)
return;
device->state = EI_DEVICE_STATE_REMOVED;
ei_remove_device(device);
}
void

View file

@ -34,9 +34,11 @@ struct ei_backend_interface {
};
enum ei_state {
EI_STATE_NEW, /* No backend yet */
EI_STATE_BACKEND, /* We have a backend */
EI_STATE_CONNECTING, /* client requested connect */
EI_STATE_CONNECTED, /* server has sent connect */
EI_STATE_DISCONNECTING, /* in the process of cleaning up */
EI_STATE_DISCONNECTED,
};

View file

@ -108,9 +108,13 @@ OBJECT_IMPLEMENT_GETTER(ei_event, type, enum ei_event_type);
_public_
OBJECT_IMPLEMENT_GETTER(ei_event, device, struct ei_device*);
static void
ei_disconnect(struct ei *ei);
static void
ei_destroy(struct ei *ei)
{
ei_disconnect(ei);
ei->logger = logger_unref(ei->logger);
if (ei->backend_interface.destroy)
ei->backend_interface.destroy(ei, ei->backend);
@ -134,7 +138,7 @@ ei_new(void *user_data)
{
_cleanup_ei_ struct ei *ei = ei_create(NULL);
ei->state = EI_STATE_BACKEND;
ei->state = EI_STATE_NEW;
list_init(&ei->event_queue);
list_init(&ei->devices);
@ -231,7 +235,8 @@ connection_send_msg(struct ei *ei, const ClientMessage *msg)
static int
connection_send_connect(struct ei *ei)
{
if (ei->state == EI_STATE_DISCONNECTED)
if (ei->state == EI_STATE_NEW ||
ei->state == EI_STATE_DISCONNECTED)
return 0;
ClientMessage msg = CLIENT_MESSAGE__INIT;
@ -246,7 +251,8 @@ connection_send_connect(struct ei *ei)
static int
connection_send_disconnect(struct ei *ei)
{
if (ei->state == EI_STATE_DISCONNECTED)
if (ei->state == EI_STATE_NEW ||
ei->state == EI_STATE_DISCONNECTED)
return 0;
ClientMessage msg = CLIENT_MESSAGE__INIT;
@ -261,7 +267,8 @@ connection_send_disconnect(struct ei *ei)
static int
connection_send_add(struct ei *ei, struct ei_device *device)
{
if (ei->state == EI_STATE_DISCONNECTED)
if (ei->state == EI_STATE_NEW ||
ei->state == EI_STATE_DISCONNECTED)
return 0;
ClientMessage msg = CLIENT_MESSAGE__INIT;
@ -279,7 +286,8 @@ connection_send_add(struct ei *ei, struct ei_device *device)
static int
connection_send_remove(struct ei *ei, struct ei_device *device)
{
if (ei->state == EI_STATE_DISCONNECTED)
if (ei->state == EI_STATE_NEW ||
ei->state == EI_STATE_DISCONNECTED)
return 0;
ClientMessage msg = CLIENT_MESSAGE__INIT;
@ -296,7 +304,8 @@ connection_send_remove(struct ei *ei, struct ei_device *device)
static int
connection_send_rel(struct ei *ei, struct ei_device *device, int32_t x, int32_t y)
{
if (ei->state == EI_STATE_DISCONNECTED)
if (ei->state == EI_STATE_NEW ||
ei->state == EI_STATE_DISCONNECTED)
return 0;
ClientMessage msg = CLIENT_MESSAGE__INIT;
@ -316,7 +325,8 @@ static int
connection_send_button(struct ei *ei, struct ei_device *device,
uint32_t b, bool is_press)
{
if (ei->state == EI_STATE_DISCONNECTED)
if (ei->state == EI_STATE_NEW ||
ei->state == EI_STATE_DISCONNECTED)
return 0;
ClientMessage msg = CLIENT_MESSAGE__INIT;
@ -336,7 +346,8 @@ static int
connection_send_key(struct ei *ei, struct ei_device *device,
uint32_t k, bool is_press)
{
if (ei->state == EI_STATE_DISCONNECTED)
if (ei->state == EI_STATE_NEW ||
ei->state == EI_STATE_DISCONNECTED)
return 0;
ClientMessage msg = CLIENT_MESSAGE__INIT;
@ -355,11 +366,28 @@ connection_send_key(struct ei *ei, struct ei_device *device,
static void
ei_disconnect(struct ei *ei)
{
connection_send_disconnect(ei);
/* FIXME: unroll the devices here */
ei_queue_disconnect_event(ei);
if (ei->state == EI_STATE_DISCONNECTED ||
ei->state == EI_STATE_DISCONNECTING)
return;
enum ei_state state = ei->state;
/* We need the disconnecting state to be re-entrant
ei_device_remove() may call ei_disconnect() on a socket error */
ei->state = EI_STATE_DISCONNECTING;
struct ei_device *d, *tmp;
list_for_each_safe(d, tmp, &ei->devices, link) {
ei_device_remove(d);
}
if (state != EI_STATE_NEW) {
connection_send_disconnect(ei);
ei_queue_disconnect_event(ei);
}
ei->state = EI_STATE_DISCONNECTED;
source_remove(ei->source);
if (ei->source)
source_remove(ei->source);
ei->source = source_unref(ei->source);
}
@ -372,6 +400,8 @@ ei_add_device(struct ei_device *device)
ei_disconnect(ei);
return rc;
}
ei_device_ref(device);
list_append(&ei->devices, &device->link);
return 0;
@ -382,15 +412,13 @@ ei_remove_device(struct ei_device *device)
{
struct ei *ei = ei_device_get_context(device);
int rc = connection_send_remove(ei, device);
if (rc) {
ei_disconnect(ei);
return rc;
}
list_remove(&device->link);
ei_device_unref(device);
return 0;
if (rc)
ei_disconnect(ei);
return rc;
}
static int
@ -669,6 +697,8 @@ connection_dispatch(struct source *source, void *userdata)
idx += len;
switch (ei->state) {
case EI_STATE_NEW:
abort();
case EI_STATE_BACKEND:
rc = connection_new_handle_msg(ei, msg);
break;
@ -678,6 +708,7 @@ connection_dispatch(struct source *source, void *userdata)
case EI_STATE_CONNECTED:
rc = connection_connected_handle_msg(ei, msg);
break;
case EI_STATE_DISCONNECTING:
case EI_STATE_DISCONNECTED:
abort();
}
@ -688,10 +719,12 @@ error:
ei_disconnect(ei);
static const char *states[] = {
"NEW",
"BACKEND",
"CONNECTING",
"CONNECTED",
"DISCONNECTED",
"DISCONNECTING",
};
if (rc)
log_warn(ei, "Connnection error: %s\n", strerror(-rc));
@ -708,6 +741,7 @@ ei_set_connection(struct ei *ei, int fd)
return -ENOMEM;
ei->source = source_ref(source);
ei->state = EI_STATE_BACKEND;
return 0;
}
@ -715,7 +749,7 @@ ei_set_connection(struct ei *ei, int fd)
_public_ void
ei_configure_name(struct ei *ei, const char *name)
{
if (ei->state != EI_STATE_BACKEND)
if (ei->state != EI_STATE_NEW)
return;
if (strlen(name) > 1024)
@ -731,10 +765,9 @@ ei_configure_name(struct ei *ei, const char *name)
static MunitResult
test_init_unref(const MunitParameter params[], void *user_data)
{
/* need to alloc here so unref can free */
struct ei *ei = ei_new(NULL);
munit_assert_int(ei->state, ==, EI_STATE_BACKEND);
munit_assert_int(ei->state, ==, EI_STATE_NEW);
munit_assert(list_empty(&ei->event_queue));
munit_assert(list_empty(&ei->devices));
@ -745,7 +778,6 @@ test_init_unref(const MunitParameter params[], void *user_data)
munit_assert_ptr_equal(ei, refd);
munit_assert_int(ei->object.refcount, ==, 2);
struct ei *unrefd = ei_unref(ei);
munit_assert_null(unrefd);
@ -758,31 +790,29 @@ test_init_unref(const MunitParameter params[], void *user_data)
static MunitResult
test_configure_name(const MunitParameter params[], void *user_data)
{
struct ei ei = {
.state = EI_STATE_NEW,
};
struct ei *ei = ei_new(NULL);
ei_configure_name(&ei, "foo");
munit_assert_string_equal(ei.name, "foo");
ei_configure_name(&ei, "bar");
munit_assert_string_equal(ei.name, "bar");
ei_configure_name(ei, "foo");
munit_assert_string_equal(ei->name, "foo");
ei_configure_name(ei, "bar");
munit_assert_string_equal(ei->name, "bar");
/* ignore names that are too long */
char buf[1200] = {0};
memset(buf, 'a', sizeof(buf) - 1);
ei_configure_name(&ei, buf);
munit_assert_string_equal(ei.name, "bar");
ei_configure_name(ei, buf);
munit_assert_string_equal(ei->name, "bar");
/* ignore names in all other states */
for (enum ei_state state = EI_STATE_HELLO + 1;
for (enum ei_state state = EI_STATE_NEW + 1;
state <= EI_STATE_DISCONNECTED;
state++) {
ei.state = state;
ei_configure_name(&ei, "expect ignored");
munit_assert_string_equal(ei.name, "bar");
ei->state = state;
ei_configure_name(ei, "expect ignored");
munit_assert_string_equal(ei->name, "bar");
}
free(ei.name);
ei_unref(ei);
return MUNIT_OK;
}