base-modem: allow parallel Enable() and Disable() requests

If additional Enable() requests are received while one is already
ongoing, we queue them and will end up completing all with the same
result once the first one finishes.

Same logic also for Disable().

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/issues/8
This commit is contained in:
Aleksander Morgado
2018-08-06 10:12:01 +02:00
committed by Dan Williams
parent 30eef126a1
commit c476612247
2 changed files with 100 additions and 23 deletions

View File

@@ -93,6 +93,10 @@ struct _MMBaseModemPrivate {
MMPortSerialAt *gps_control;
MMPortSerialGps *gps;
/* Support for parallel enable/disable operations */
GList *enable_tasks;
GList *disable_tasks;
#if defined WITH_QMI
/* QMI ports */
GList *qmi;
@@ -314,49 +318,123 @@ mm_base_modem_grab_port (MMBaseModem *self,
}
gboolean
mm_base_modem_disable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
mm_base_modem_disable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return MM_BASE_MODEM_GET_CLASS (self)->disable_finish (self, res, error);
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disable_ready (MMBaseModem *self,
GAsyncResult *res)
{
GError *error = NULL;
GList *l;
GList *disable_tasks;
g_assert (self->priv->disable_tasks);
disable_tasks = self->priv->disable_tasks;
self->priv->disable_tasks = NULL;
MM_BASE_MODEM_GET_CLASS (self)->disable_finish (self, res, &error);
for (l = disable_tasks; l; l = g_list_next (l)) {
if (error)
g_task_return_error (G_TASK (l->data), g_error_copy (error));
else
g_task_return_boolean (G_TASK (l->data), TRUE);
}
g_clear_error (&error);
g_list_free_full (disable_tasks, (GDestroyNotify)g_object_unref);
}
void
mm_base_modem_disable (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
mm_base_modem_disable (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
gboolean run_disable;
g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable != NULL);
g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable_finish != NULL);
/* If the list of disable tasks is empty, we need to run */
run_disable = !self->priv->disable_tasks;
/* Store task */
task = g_task_new (self, self->priv->cancellable, callback, user_data);
self->priv->disable_tasks = g_list_append (self->priv->disable_tasks, task);
if (!run_disable)
return;
MM_BASE_MODEM_GET_CLASS (self)->disable (
self,
self->priv->cancellable,
callback,
user_data);
(GAsyncReadyCallback) disable_ready,
NULL);
}
gboolean
mm_base_modem_enable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
mm_base_modem_enable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return MM_BASE_MODEM_GET_CLASS (self)->enable_finish (self, res, error);
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
enable_ready (MMBaseModem *self,
GAsyncResult *res)
{
GError *error = NULL;
GList *l;
GList *enable_tasks;
g_assert (self->priv->enable_tasks);
enable_tasks = self->priv->enable_tasks;
self->priv->enable_tasks = NULL;
MM_BASE_MODEM_GET_CLASS (self)->enable_finish (self, res, &error);
for (l = enable_tasks; l; l = g_list_next (l)) {
if (error)
g_task_return_error (G_TASK (l->data), g_error_copy (error));
else
g_task_return_boolean (G_TASK (l->data), TRUE);
}
g_clear_error (&error);
g_list_free_full (enable_tasks, (GDestroyNotify)g_object_unref);
}
void
mm_base_modem_enable (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
mm_base_modem_enable (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
gboolean run_enable;
g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable != NULL);
g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable_finish != NULL);
/* If the list of enable tasks is empty, we need to run */
run_enable = !self->priv->enable_tasks;
/* Store task */
task = g_task_new (self, self->priv->cancellable, callback, user_data);
self->priv->enable_tasks = g_list_append (self->priv->enable_tasks, task);
if (!run_enable)
return;
MM_BASE_MODEM_GET_CLASS (self)->enable (
self,
self->priv->cancellable,
callback,
user_data);
(GAsyncReadyCallback) enable_ready,
NULL);
}
gboolean
@@ -1423,6 +1501,9 @@ finalize (GObject *object)
* mm_auth_provider_cancel_for_owner (self->priv->authp, object);
*/
g_assert (!self->priv->enable_tasks);
g_assert (!self->priv->disable_tasks);
mm_dbg ("Modem (%s) '%s' completely disposed",
self->priv->plugin,
self->priv->device);

View File

@@ -10006,11 +10006,7 @@ enable (MMBaseModem *self,
break;
case MM_MODEM_STATE_ENABLING:
g_task_return_new_error (task,
MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Cannot enable modem: "
"already being enabled");
g_assert_not_reached ();
break;
case MM_MODEM_STATE_ENABLED: