context: Simplify handling of removal signals

We used to notify device removed signal in an idle to ensure that this
happened before the context device-removed signal, however this can be
achieved also using the after-callbacks without having to imply further
idles that complicates the context destruction handling, also causing
that a device that has been removed is returned by get_devices() for
longer than expected.

Simplify the codepath adjusting the tests. They are still checking that
the order is preserved.
This commit is contained in:
Marco Trevisan (Treviño) 2023-06-22 00:48:45 -04:00 committed by Marco Trevisan
parent 3b783b9985
commit 51dd46b3d9
2 changed files with 21 additions and 96 deletions

View file

@ -89,54 +89,17 @@ is_driver_allowed (const gchar *driver)
return g_strv_contains ((const gchar * const *) allowlisted_drivers, driver);
}
typedef struct
static void
remove_device (FpContext *context,
FpDevice *device)
{
FpContext *context;
FpDevice *device;
GSource *source;
} RemoveDeviceData;
static gboolean
remove_device_idle_cb (RemoveDeviceData *data)
{
FpContextPrivate *priv = fp_context_get_instance_private (data->context);
FpContextPrivate *priv = fp_context_get_instance_private (context);
guint idx = 0;
g_return_val_if_fail (g_ptr_array_find (priv->devices, data->device, &idx), G_SOURCE_REMOVE);
g_return_if_fail (g_ptr_array_find (priv->devices, device, &idx));
g_signal_emit (data->context, signals[DEVICE_REMOVED_SIGNAL], 0, data->device);
g_signal_emit (context, signals[DEVICE_REMOVED_SIGNAL], 0, device);
g_ptr_array_remove_index_fast (priv->devices, idx);
return G_SOURCE_REMOVE;
}
static void
remove_device_data_free (RemoveDeviceData *data)
{
FpContextPrivate *priv = fp_context_get_instance_private (data->context);
priv->sources = g_slist_remove (priv->sources, data->source);
g_free (data);
}
static void
remove_device (FpContext *context, FpDevice *device)
{
g_autoptr(GSource) source = NULL;
FpContextPrivate *priv = fp_context_get_instance_private (context);
RemoveDeviceData *data;
data = g_new (RemoveDeviceData, 1);
data->context = context;
data->device = device;
source = data->source = g_idle_source_new ();
g_source_set_callback (source,
G_SOURCE_FUNC (remove_device_idle_cb), data,
(GDestroyNotify) remove_device_data_free);
g_source_attach (source, g_main_context_get_thread_default ());
priv->sources = g_slist_prepend (priv->sources, source);
}
static void
@ -149,17 +112,13 @@ device_remove_on_notify_open_cb (FpContext *context, GParamSpec *pspec, FpDevice
static void
device_removed_cb (FpContext *context, FpDevice *device)
{
gboolean open = FALSE;
g_object_get (device, "open", &open, NULL);
/* Wait for device close if the device is currently still open. */
if (open)
if (fp_device_is_open (device))
{
g_signal_connect_object (device, "notify::open",
(GCallback) device_remove_on_notify_open_cb,
context,
G_CONNECT_SWAPPED);
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
}
else
{
@ -195,7 +154,7 @@ async_device_init_done_cb (GObject *source_object, GAsyncResult *res, gpointer u
g_signal_connect_object (device, "removed",
(GCallback) device_removed_cb,
context,
G_CONNECT_SWAPPED);
G_CONNECT_SWAPPED | G_CONNECT_AFTER);
g_signal_emit (context, signals[DEVICE_ADDED_SIGNAL], 0, device);
}
@ -275,8 +234,10 @@ usb_device_removed_cb (FpContext *self, GUsbDevice *device, GUsbContext *usb_ctx
if (cls->type != FP_DEVICE_TYPE_USB)
continue;
if (fpi_device_get_usb_device (dev) == device)
fpi_device_remove (dev);
if (fpi_device_get_usb_device (dev) != device)
continue;
fpi_device_remove (dev);
}
}

View file

@ -121,29 +121,21 @@ static void
test_context_remove_device_closed (void)
{
g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_device (FPT_VIRTUAL_DEVICE_IMAGE);
gboolean removed;
FpDevice *device = tctx->device;
tctx->user_data = NULL;
g_signal_connect (tctx->device, "removed", (GCallback) device_removed_cb, tctx);
g_signal_connect (tctx->fp_context, "device-removed", (GCallback) context_device_removed_cb, tctx);
g_signal_connect (tctx->device, "removed", (GCallback) device_removed_cb, tctx);
/* Triggering remove on closed device. */
fpi_device_remove (tctx->device);
g_assert_nonnull (tctx->device);
g_object_get (tctx->device, "removed", &removed, NULL);
g_assert_true (removed);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, DEV_REMOVED_CB);
/* device-removed is dispatched from idle. */
while (g_main_context_iteration (NULL, FALSE))
{
}
/* The device is now destroyed and device-removed was called. */
g_assert_null (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, CTX_DEVICE_REMOVED_CB);
g_assert_false (g_ptr_array_find (fp_context_get_devices (tctx->fp_context), device, NULL));
fpt_teardown_virtual_device_environment ();
}
@ -165,6 +157,7 @@ test_context_remove_device_closing (void)
g_autoptr(FptContext) tctx = fpt_context_new_with_virtual_device (FPT_VIRTUAL_DEVICE_IMAGE);
g_autoptr(GError) close_error = NULL;
g_autoptr(GError) error = NULL;
FpDevice *device = tctx->device;
gboolean removed;
tctx->user_data = NULL;
@ -190,17 +183,11 @@ test_context_remove_device_closing (void)
g_assert_error (close_error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_REMOVED);
/* Now the removed callback has been called already. */
g_assert_nonnull (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, DEV_REMOVED_CB);
/* While device-removed needs another idle iteration. */
while (g_main_context_iteration (NULL, FALSE))
{
}
g_assert_null (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, CTX_DEVICE_REMOVED_CB);
g_assert_false (g_ptr_array_find (fp_context_get_devices (tctx->fp_context), device, NULL));
fpt_teardown_virtual_device_environment ();
}
@ -234,16 +221,9 @@ test_context_remove_device_open (void)
}
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, DEV_REMOVED_CB);
/* On close, the device will be removed from the context,
* but only a main loop iteration later. */
/* On close, the device will be removed from the context */
fp_device_close_sync (tctx->device, NULL, &error);
g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_REMOVED);
g_assert_nonnull (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, DEV_REMOVED_CB);
while (g_main_context_iteration (NULL, FALSE))
{
}
g_assert_null (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, CTX_DEVICE_REMOVED_CB);
@ -298,14 +278,6 @@ test_context_remove_device_opening (void)
fp_device_close_sync (tctx->device, NULL, &close_error);
g_assert_error (close_error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_REMOVED);
g_assert_nonnull (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, DEV_REMOVED_CB);
/* The device-removed signal needs an idle iteration. */
while (g_main_context_iteration (NULL, FALSE))
{
}
g_assert_null (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, CTX_DEVICE_REMOVED_CB);
@ -369,14 +341,6 @@ test_context_remove_device_active (void)
/* Now we close the device, state remains unchanged mostly. */
fp_device_close_sync (tctx->device, NULL, &error);
g_assert_error (error, FP_DEVICE_ERROR, FP_DEVICE_ERROR_REMOVED);
g_assert_nonnull (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, DEV_REMOVED_CB);
/* And "device-removed" is called */
while (g_main_context_iteration (NULL, FALSE))
{
}
g_assert_null (tctx->device);
g_assert_cmpint (GPOINTER_TO_INT (tctx->user_data), ==, CTX_DEVICE_REMOVED_CB);