eis: fix an error when the seat is removed twice

Previous sequence reproducible with the eis-demo-server and
ei-demo-client:
- ctrl+c the client
- eis client dispatch fails with an rc < 0 due to the socket being closed
- eis calls eis_client_disconnect() which calls eis_seat_removed()
  - that triggers an event for `SEAT_UNBIND`
  - seat is set to REMOVE
- eis-demo-server gets the `SEAT_UNBIND` event and (correctly) calls
  eis_seat_remove() since it doesn't know the client is disconnected
  yet. That is due to the library unwinding the state transparently.

Fix this by splitting out the calls: eis_drop_seat() is the internally
used one that sets it to a new state of REMOVED_INTERNALLY, and then
eis_seat_remove merely updates the state.

Fixes #10

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2022-02-25 15:45:07 +10:00
parent 6f3a62f5d2
commit 7a91f27d42
3 changed files with 31 additions and 11 deletions

View file

@ -190,8 +190,7 @@ eis_client_disconnect(struct eis_client *client)
{
struct eis_seat *s;
list_for_each_safe(s, &client->seats, link) {
eis_seat_unbind(s);
eis_seat_remove(s);
eis_seat_drop(s);
}
}
eis_queue_disconnect_event(client);

View file

@ -87,6 +87,7 @@ enum eis_seat_state {
EIS_SEAT_STATE_ADDED,
EIS_SEAT_STATE_BOUND,
EIS_SEAT_STATE_UNBOUND,
EIS_SEAT_STATE_REMOVED_INTERNALLY, /* Removed internally but eis_seat_remove() may be called */
EIS_SEAT_STATE_REMOVED, /* Removed but still waiting for some devices to be removed */
EIS_SEAT_STATE_DEAD, /* Removed from our list */
};
@ -251,6 +252,9 @@ eis_seat_bind(struct eis_seat *seat, uint32_t cap);
void
eis_seat_unbind(struct eis_seat *seat);
void
eis_seat_drop(struct eis_seat *seat);
struct eis *
eis_device_get_context(struct eis_device *device);

View file

@ -90,6 +90,7 @@ eis_seat_add(struct eis_seat *seat)
case EIS_SEAT_STATE_BOUND:
case EIS_SEAT_STATE_UNBOUND:
case EIS_SEAT_STATE_REMOVED:
case EIS_SEAT_STATE_REMOVED_INTERNALLY:
case EIS_SEAT_STATE_DEAD:
log_bug_client(eis_client_get_context(client),
"%s: seat already added/removed/dead\n", __func__);
@ -112,6 +113,7 @@ eis_seat_bind(struct eis_seat *seat, uint32_t caps)
case EIS_SEAT_STATE_BOUND:
case EIS_SEAT_STATE_UNBOUND:
case EIS_SEAT_STATE_REMOVED:
case EIS_SEAT_STATE_REMOVED_INTERNALLY:
case EIS_SEAT_STATE_DEAD:
log_bug_client(eis_client_get_context(client),
"%s: seat cannot be bound\n", __func__);
@ -134,6 +136,7 @@ eis_seat_unbind(struct eis_seat *seat)
case EIS_SEAT_STATE_PENDING:
case EIS_SEAT_STATE_UNBOUND:
case EIS_SEAT_STATE_REMOVED:
case EIS_SEAT_STATE_REMOVED_INTERNALLY:
case EIS_SEAT_STATE_DEAD:
log_bug_client(eis_client_get_context(client),
"%s: seat cannot be unbound\n", __func__);
@ -144,16 +147,39 @@ eis_seat_unbind(struct eis_seat *seat)
eis_queue_seat_unbind_event(seat);
}
void
eis_seat_drop(struct eis_seat *seat)
{
if (seat->state == EIS_SEAT_STATE_BOUND)
eis_seat_unbind(seat);
struct eis_device *d;
list_for_each_safe(d, &seat->devices, link) {
eis_device_remove(d);
}
eis_client_remove_seat(eis_seat_get_client(seat), seat);
list_remove(&seat->link);
seat->state = EIS_SEAT_STATE_REMOVED_INTERNALLY;
eis_seat_unref(seat);
}
_public_ void
eis_seat_remove(struct eis_seat *seat)
{
struct eis_client *client = eis_seat_get_client(seat);
_unref_(eis_seat) *s = eis_seat_ref(seat);
switch (seat->state) {
case EIS_SEAT_STATE_PENDING:
case EIS_SEAT_STATE_ADDED:
case EIS_SEAT_STATE_BOUND:
case EIS_SEAT_STATE_UNBOUND:
eis_seat_drop(s);
s->state = EIS_SEAT_STATE_REMOVED;
break;
case EIS_SEAT_STATE_REMOVED_INTERNALLY:
s->state = EIS_SEAT_STATE_REMOVED;
break;
case EIS_SEAT_STATE_REMOVED:
case EIS_SEAT_STATE_DEAD:
@ -162,15 +188,6 @@ eis_seat_remove(struct eis_seat *seat)
return;
}
struct eis_device *d;
list_for_each_safe(d, &seat->devices, link) {
eis_device_remove(d);
}
eis_client_remove_seat(eis_seat_get_client(seat), seat);
seat->state = EIS_SEAT_STATE_REMOVED;
list_remove(&seat->link);
eis_seat_unref(seat);
}
_public_ void