device: Complete driver operations in idle

When drivers signal that an action is completed, they might be still in the
middle of a SSM and so its callback might not be called properly as part of
the completion.

This could make impossible to chain operations like open->enroll/list with
some drivers (hey, synaptics, I'm looking at you!) when the next operation
is called from the GAsyncReadyCallback of the previous call.

To avoid this to happen, ensure that when a driver completes an operation,
we handle the notification to the caller in a next idle iteration, not to
end up in a possible broken state.

Abstract this by using a function that handles the task return for each
used task type to avoid duplicating similar functions doing all the same
thing
This commit is contained in:
Marco Trevisan (Treviño) 2019-11-18 19:09:54 +01:00 committed by Benjamin Berg
parent 728335581f
commit ad920f9597

View file

@ -72,6 +72,7 @@ typedef struct
GAsyncReadyCallback current_user_cb;
gulong current_cancellable_id;
GSource *current_idle_cancel_source;
GSource *current_task_idle_return_source;
/* State for tasks */
gboolean wait_for_finger;
@ -312,8 +313,8 @@ maybe_cancel_on_cancelled (FpDevice *device,
NULL);
}
static GTask*
reset_device_state (FpDevice *device)
static void
clear_device_cancel_action (FpDevice *device)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
@ -325,10 +326,6 @@ reset_device_state (FpDevice *device)
priv->current_cancellable_id);
priv->current_cancellable_id = 0;
}
priv->current_action = FP_DEVICE_ACTION_NONE;
return g_steal_pointer (&priv->current_task);
}
static void
@ -361,6 +358,9 @@ fp_device_finalize (GObject *object)
g_slist_free_full (priv->sources, (GDestroyNotify) g_source_destroy);
g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
g_clear_pointer (&priv->current_task_idle_return_source, g_source_destroy);
g_clear_pointer (&priv->device_id, g_free);
g_clear_pointer (&priv->device_name, g_free);
@ -1792,6 +1792,97 @@ fpi_device_action_error (FpDevice *device,
}
}
typedef enum _FpDeviceTaskReturnType {
FP_DEVICE_TASK_RETURN_INT,
FP_DEVICE_TASK_RETURN_BOOL,
FP_DEVICE_TASK_RETURN_OBJECT,
FP_DEVICE_TASK_RETURN_PTR_ARRAY,
FP_DEVICE_TASK_RETURN_ERROR,
} FpDeviceTaskReturnType;
typedef struct _FpDeviceTaskReturnData
{
FpDevice *device;
FpDeviceTaskReturnType type;
gpointer result;
} FpDeviceTaskReturnData;
static gboolean
fp_device_task_return_in_idle_cb (gpointer user_data)
{
FpDeviceTaskReturnData *data = user_data;
FpDevicePrivate *priv = fp_device_get_instance_private (data->device);
g_autoptr(GTask) task = NULL;
g_debug ("Completing action %d in idle!", priv->current_action);
task = g_steal_pointer (&priv->current_task);
priv->current_action = FP_DEVICE_ACTION_NONE;
priv->current_task_idle_return_source = NULL;
switch (data->type)
{
case FP_DEVICE_TASK_RETURN_INT:
g_task_return_int (task, GPOINTER_TO_INT (data->result));
break;
case FP_DEVICE_TASK_RETURN_BOOL:
g_task_return_boolean (task, GPOINTER_TO_UINT (data->result));
break;
case FP_DEVICE_TASK_RETURN_OBJECT:
g_task_return_pointer (task, data->result, g_object_unref);
break;
case FP_DEVICE_TASK_RETURN_PTR_ARRAY:
g_task_return_pointer (task, data->result,
(GDestroyNotify) g_ptr_array_unref);
break;
case FP_DEVICE_TASK_RETURN_ERROR:
g_task_return_error (task, data->result);
break;
default:
g_assert_not_reached ();
}
return G_SOURCE_REMOVE;
}
static void
fp_device_task_return_data_free (FpDeviceTaskReturnData *data)
{
g_object_unref (data->device);
g_free (data);
}
static void
fp_device_return_task_in_idle (FpDevice *device,
FpDeviceTaskReturnType return_type,
gpointer return_data)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpDeviceTaskReturnData *data;
data = g_new0 (FpDeviceTaskReturnData, 1);
data->device = g_object_ref (device);
data->type = return_type;
data->result = return_data;
priv->current_task_idle_return_source = g_idle_source_new ();
g_source_set_priority (priv->current_task_idle_return_source,
g_task_get_priority (priv->current_task));
g_source_set_callback (priv->current_task_idle_return_source,
fp_device_task_return_in_idle_cb,
data,
(GDestroyNotify) fp_device_task_return_data_free);
g_source_attach (priv->current_task_idle_return_source, NULL);
g_source_unref (priv->current_task_idle_return_source);
}
/**
* fpi_device_probe_complete:
* @device: The #FpDevice
@ -1809,14 +1900,12 @@ fpi_device_probe_complete (FpDevice *device,
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_PROBE);
g_debug ("Device reported probe completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
if (!error)
{
@ -1832,11 +1921,12 @@ fpi_device_probe_complete (FpDevice *device,
priv->device_name = g_strdup (device_name);
g_object_notify_by_pspec (G_OBJECT (device), properties[PROP_NAME]);
}
g_task_return_boolean (task, TRUE);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
GUINT_TO_POINTER (TRUE));
}
else
{
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
}
@ -1852,22 +1942,21 @@ fpi_device_open_complete (FpDevice *device, GError *error)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_OPEN);
g_debug ("Device reported open completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
if (!error)
priv->is_open = TRUE;
if (!error)
g_task_return_boolean (task, TRUE);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
GUINT_TO_POINTER (TRUE));
else
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
/**
@ -1880,7 +1969,6 @@ fpi_device_open_complete (FpDevice *device, GError *error)
void
fpi_device_close_complete (FpDevice *device, GError *error)
{
g_autoptr(GTask) task = NULL;
GError *nested_error = NULL;
FpDevicePrivate *priv = fp_device_get_instance_private (device);
@ -1889,7 +1977,7 @@ fpi_device_close_complete (FpDevice *device, GError *error)
g_debug ("Device reported close completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
priv->is_open = FALSE;
switch (priv->type)
@ -1899,7 +1987,7 @@ fpi_device_close_complete (FpDevice *device, GError *error)
{
if (error == NULL)
error = nested_error;
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
return;
}
break;
@ -1909,14 +1997,16 @@ fpi_device_close_complete (FpDevice *device, GError *error)
default:
g_assert_not_reached ();
g_task_return_error (task, fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR,
fpi_device_error_new (FP_DEVICE_ERROR_GENERAL));
return;
}
if (!error)
g_task_return_boolean (task, TRUE);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
GUINT_TO_POINTER (TRUE));
else
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
/**
@ -1933,33 +2023,30 @@ fpi_device_enroll_complete (FpDevice *device, FpPrint *print, GError *error)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_ENROLL);
g_debug ("Device reported enroll completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
if (!error)
{
if (FP_IS_PRINT (print))
{
g_task_return_pointer (task, print, g_object_unref);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, print);
}
else
{
g_warning ("Driver did not provide a valid print and failed to provide an error!");
g_task_return_new_error (task,
FP_DEVICE_ERROR,
FP_DEVICE_ERROR_GENERAL,
"Driver failed to provide print data!");
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Driver failed to provide print data!");
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
}
else
{
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
if (FP_IS_PRINT (print))
{
g_warning ("Driver passed an error but also provided a print, returning error!");
@ -1986,16 +2073,14 @@ fpi_device_verify_complete (FpDevice *device,
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_VERIFY);
g_debug ("Device reported verify completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
g_object_set_data_full (G_OBJECT (task),
g_object_set_data_full (G_OBJECT (priv->current_task),
"print",
print,
g_object_unref);
@ -2004,20 +2089,20 @@ fpi_device_verify_complete (FpDevice *device,
{
if (result != FPI_MATCH_ERROR)
{
g_task_return_int (task, result);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_INT,
GINT_TO_POINTER (result));
}
else
{
g_warning ("Driver did not provide an error for a failed verify operation!");
g_task_return_new_error (task,
FP_DEVICE_ERROR,
FP_DEVICE_ERROR_GENERAL,
"Driver failed to provide an error!");
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Driver failed to provide an error!");
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
}
else
{
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
if (result != FPI_MATCH_ERROR)
{
g_warning ("Driver passed an error but also provided a match result, returning error!");
@ -2045,30 +2130,29 @@ fpi_device_identify_complete (FpDevice *device,
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_IDENTIFY);
g_debug ("Device reported identify completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
g_object_set_data_full (G_OBJECT (task),
g_object_set_data_full (G_OBJECT (priv->current_task),
"print",
print,
g_object_unref);
g_object_set_data_full (G_OBJECT (task),
g_object_set_data_full (G_OBJECT (priv->current_task),
"match",
match,
g_object_unref);
if (!error)
{
g_task_return_boolean (task, TRUE);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
GUINT_TO_POINTER (TRUE));
}
else
{
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
if (match)
{
g_warning ("Driver passed an error but also provided a match result, returning error!");
@ -2093,33 +2177,30 @@ fpi_device_capture_complete (FpDevice *device,
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_CAPTURE);
g_debug ("Device reported capture completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
if (!error)
{
if (image)
{
g_task_return_pointer (task, image, g_object_unref);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_OBJECT, image);
}
else
{
g_warning ("Driver did not provide an error for a failed capture operation!");
g_task_return_new_error (task,
FP_DEVICE_ERROR,
FP_DEVICE_ERROR_GENERAL,
"Driver failed to provide an error!");
error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Driver failed to provide an error!");
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
}
else
{
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
if (image)
{
g_warning ("Driver passed an error but also provided an image, returning error!");
@ -2141,19 +2222,18 @@ fpi_device_delete_complete (FpDevice *device,
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_autoptr(GTask) task = NULL;
g_return_if_fail (FP_IS_DEVICE (device));
g_return_if_fail (priv->current_action == FP_DEVICE_ACTION_DELETE);
g_debug ("Device reported deletion completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
if (!error)
g_task_return_boolean (task, TRUE);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_BOOL,
GUINT_TO_POINTER (TRUE));
else
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
/**
@ -2174,7 +2254,6 @@ fpi_device_list_complete (FpDevice *device,
GPtrArray *prints,
GError *error)
{
g_autoptr(GTask) task = NULL;
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_return_if_fail (FP_IS_DEVICE (device));
@ -2182,7 +2261,7 @@ fpi_device_list_complete (FpDevice *device,
g_debug ("Device reported listing completion");
task = reset_device_state (device);
clear_device_cancel_action (device);
if (prints && error)
{
@ -2197,9 +2276,9 @@ fpi_device_list_complete (FpDevice *device,
}
if (!error)
g_task_return_pointer (task, prints, (GDestroyNotify) g_ptr_array_unref);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_PTR_ARRAY, prints);
else
g_task_return_error (task, error);
fp_device_return_task_in_idle (device, FP_DEVICE_TASK_RETURN_ERROR, error);
}
/**