device: Add device critical section API

This commit is contained in:
Benjamin Berg 2021-04-23 09:48:39 +02:00
parent 23a4f5b77a
commit 77756e111d
4 changed files with 110 additions and 1 deletions

View file

@ -79,6 +79,11 @@ typedef struct
gboolean wait_for_finger; gboolean wait_for_finger;
FpFingerStatusFlags finger_status; FpFingerStatusFlags finger_status;
/* Driver critical sections */
guint critical_section;
GSource *critical_section_flush_source;
gboolean cancel_queued;
/* Device temperature model information and state */ /* Device temperature model information and state */
GSource *temp_timeout; GSource *temp_timeout;
FpTemperature temp_current; FpTemperature temp_current;

View file

@ -94,7 +94,10 @@ fp_device_cancel_in_idle_cb (gpointer user_data)
priv->current_idle_cancel_source = NULL; priv->current_idle_cancel_source = NULL;
cls->cancel (self); if (priv->critical_section)
priv->cancel_queued = TRUE;
else
cls->cancel (self);
fpi_device_report_finger_status (self, FP_FINGER_STATUS_NONE); fpi_device_report_finger_status (self, FP_FINGER_STATUS_NONE);

View file

@ -808,6 +808,104 @@ fpi_device_action_error (FpDevice *device,
} }
} }
/**
* fpi_device_critical_enter:
* @device: The #FpDevice
*
* Enter a critical section in the driver code where no outside calls from
* libfprint should happen. Drivers can already assume that everything
* happens from the same thread, however, that still allows e.g. the cancel
* vfunc to be called at any point in time.
*
* Using this kind of critical section, the driver can assume that libfprint
* will not forward any external requests to the driver for the time being.
* This is for example useful to prevent cancellation while the device is being
* set up. Or, said differently, using this feature means that the cancel
* handler is able to make more assumptions about the current state.
*
* Please note that the driver is not shielded from all external changes. For
* example the cancellable as returned by fpi_device_get_cancellable() will
* still change immediately.
*
* The driver may call this function multiple times, but must also ensure that
* fpi_device_critical_leave() is called an equal amount of times and that all
* critical sections are left before command completion.
*/
void
fpi_device_critical_enter (FpDevice *device)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_return_if_fail (priv->current_action != FPI_DEVICE_ACTION_NONE);
priv->critical_section += 1;
/* Stop flushing events if that was previously queued. */
if (priv->critical_section_flush_source)
g_source_destroy (priv->critical_section_flush_source);
priv->critical_section_flush_source = NULL;
}
static gboolean
fpi_device_critical_section_flush_idle_cb (FpDevice *device)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
FpDeviceClass *cls = FP_DEVICE_GET_CLASS (device);
if (priv->cancel_queued)
{
/* Cancellation must only happen if the driver is busy. */
if (priv->current_action != FPI_DEVICE_ACTION_NONE &&
priv->current_task_idle_return_source == NULL)
cls->cancel (device);
priv->cancel_queued = FALSE;
return G_SOURCE_CONTINUE;
}
priv->critical_section_flush_source = NULL;
return G_SOURCE_REMOVE;
}
/**
* fpi_device_critical_leave:
* @device: The #FpDevice
*
* Leave a critical section started by fpi_device_critical_enter().
*
* Once all critical sections have been left, libfprint will start flushing
* out the queued up requests. This is done from the mainloop and the driver
* is protected from reentrency issues.
*/
void
fpi_device_critical_leave (FpDevice *device)
{
FpDevicePrivate *priv = fp_device_get_instance_private (device);
g_return_if_fail (priv->current_action != FPI_DEVICE_ACTION_NONE);
g_return_if_fail (priv->critical_section);
priv->critical_section -= 1;
if (priv->critical_section)
return;
/* We left the critical section, make sure a flush is queued. */
if (priv->critical_section_flush_source)
return;
priv->critical_section_flush_source = g_idle_source_new ();
g_source_set_callback (priv->critical_section_flush_source,
(GSourceFunc) fpi_device_critical_section_flush_idle_cb,
device,
NULL);
g_source_set_name (priv->critical_section_flush_source,
"Flush libfprint driver critical section");
g_source_attach (priv->critical_section_flush_source,
g_task_get_context (priv->current_task));
g_source_unref (priv->critical_section_flush_source);
}
static void static void
clear_device_cancel_action (FpDevice *device) clear_device_cancel_action (FpDevice *device)
{ {

View file

@ -264,6 +264,9 @@ void fpi_device_update_features (FpDevice *device,
void fpi_device_action_error (FpDevice *device, void fpi_device_action_error (FpDevice *device,
GError *error); GError *error);
void fpi_device_critical_enter (FpDevice *device);
void fpi_device_critical_leave (FpDevice *device);
void fpi_device_probe_complete (FpDevice *device, void fpi_device_probe_complete (FpDevice *device,
const gchar *device_id, const gchar *device_id,
const gchar *device_name, const gchar *device_name,