base-modem: synchronize initialize/enable/disable/sync operations

Each of these now holds an exclusive lock, so all these operations are
synchronized.

This logic can be tested by queing different enable/disable operations
one after the other, e.g.:
  mmcli -m a -d &    --> Disable 1
  sleep 0.2
  mmcli -m a -e &    --> Enable 1
  sleep 0.2
  mmcli -m a -e &    --> Enable 2
  sleep 0.2
  mmcli -m a -d &    --> Disable 2
  sleep 0.2
  mmcli -m a -e &    --> Enable 3
  sleep 0.2
  mmcli -m a -d &    --> Disable 3

This previous sequence produces MM logs as follows:
  <dbg> [1726566352.936025] [modem0] [operation 9] default - disable:  scheduled     --> Disable 1 requested
  <dbg> [1726566352.936399] [modem0] [operation 9] default - disable:  lock acquired --> Disable 1 started
  <dbg> [1726566353.136445] [modem0] [operation 10] default - enable:  scheduled     --> Enable 1 requested
  <dbg> [1726566353.202980] [modem0] [operation 9] default - disable:  lock released --> Disable 1 finished
  <dbg> [1726566353.203526] [modem0] [operation 10] default - enable:  lock acquired --> Enable 1 started
  <dbg> [1726566353.320057] [modem0] [operation 11] default - enable:  scheduled     --> Enable 2 requested
  <dbg> [1726566353.440931] [modem0] [operation 10] default - enable:  lock released --> Enable 1 finished
  <dbg> [1726566353.443238] [modem0] [operation 11] default - enable:  lock acquired --> Enable 2 started
  <dbg> [1726566353.452984] [modem0] [operation 11] default - enable:  lock released --> Enable 2 finished
  <dbg> [1726566353.517512] [modem0] [operation 12] default - disable: scheduled     --> Disable 2 requested
  <dbg> [1726566353.517699] [modem0] [operation 12] default - disable: lock acquired --> Disable 2 started
  <dbg> [1726566353.688695] [modem0] [operation 12] default - disable: lock released --> Disable 2 finished
  <dbg> [1726566353.718237] [modem0] [operation 13] default - enable:  scheduled     --> Enable 3 requested
  <dbg> [1726566353.718417] [modem0] [operation 13] default - enable:  lock acquired --> Enable 3 started
  <dbg> [1726566353.937122] [modem0] [operation 14] default - disable: scheduled     --> Disable 3 requested
  <dbg> [1726566353.970791] [modem0] [operation 13] default - enable:  lock released --> Enable 3 finished
  <dbg> [1726566353.970964] [modem0] [operation 14] default - disable: lock acquired --> Disable 3 started
  <dbg> [1726566354.170938] [modem0] [operation 14] default - disable: lock released --> Disable 3 finished
This commit is contained in:
Aleksander Morgado
2024-09-30 07:55:05 +00:00
parent 8e0cbd9c94
commit be668462c0
7 changed files with 281 additions and 129 deletions
+207 -66
View File
@@ -711,6 +711,167 @@ mm_base_modem_wait_link_port (MMBaseModem *self,
mm_obj_dbg (self, "waiting for port '%s/%s'...", subsystem, name);
}
/******************************************************************************/
/* Common support to perform state update/sync operations with the base modem. */
typedef enum {
STATE_OPERATION_TYPE_INITIALIZE,
STATE_OPERATION_TYPE_ENABLE,
STATE_OPERATION_TYPE_DISABLE,
#if defined WITH_SUSPEND_RESUME
STATE_OPERATION_TYPE_SYNC,
#endif
} StateOperationType;
typedef struct {
StateOperation operation;
StateOperationFinish operation_finish;
gssize operation_id;
} StateOperationContext;
static void
state_operation_context_free (StateOperationContext *ctx)
{
g_assert (ctx->operation_id < 0);
g_slice_free (StateOperationContext, ctx);
}
static gboolean
state_operation_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
state_operation_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
StateOperationContext *ctx;
ctx = g_task_get_task_data (task);
if (ctx->operation_id >= 0) {
mm_base_modem_operation_unlock (self, ctx->operation_id);
ctx->operation_id = (gssize) -1;
}
if (!ctx->operation_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void
state_operation_run (GTask *task)
{
MMBaseModem *self;
StateOperationContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
ctx->operation (self,
self->priv->cancellable,
(GAsyncReadyCallback) state_operation_ready,
task);
}
static void
lock_before_state_operation_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
StateOperationContext *ctx;
ctx = g_task_get_task_data (task);
ctx->operation_id = mm_base_modem_operation_lock_finish (self, res, &error);
if (ctx->operation_id < 0) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
state_operation_run (task);
}
static void
state_operation (MMBaseModem *self,
StateOperationType operation_type,
MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
StateOperationContext *ctx;
gboolean optional;
const gchar *operation_description;
ctx = g_slice_new0 (StateOperationContext);
ctx->operation_id = (gssize) -1;
/* configure operation to run */
switch (operation_type) {
case STATE_OPERATION_TYPE_INITIALIZE:
operation_description = "initialization";
optional = FALSE;
ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->initialize;
ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->initialize_finish;
break;
case STATE_OPERATION_TYPE_ENABLE:
operation_description = "enabling";
optional = FALSE;
ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->enable;
ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->enable_finish;
break;
case STATE_OPERATION_TYPE_DISABLE:
operation_description = "disabling";
optional = FALSE;
ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->disable;
ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->disable_finish;
break;
#if defined WITH_SUSPEND_RESUME
case STATE_OPERATION_TYPE_SYNC:
operation_description = "sync";
optional = TRUE;
ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->sync;
ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->sync_finish;
break;
#endif
default:
g_assert_not_reached ();
}
task = g_task_new (self, NULL, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify) state_operation_context_free);
if (optional && (!ctx->operation || !ctx->operation_finish)) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported");
g_object_unref (task);
return;
}
g_assert (ctx->operation && ctx->operation_finish);
if (operation_lock == MM_BASE_MODEM_OPERATION_LOCK_ALREADY_ACQUIRED) {
state_operation_run (task);
return;
}
g_assert (operation_lock == MM_BASE_MODEM_OPERATION_LOCK_REQUIRED);
mm_base_modem_operation_lock (self,
MM_BASE_MODEM_OPERATION_PRIORITY_DEFAULT,
operation_description,
(GAsyncReadyCallback) lock_before_state_operation_ready,
task);
}
/******************************************************************************/
#if defined WITH_SUSPEND_RESUME
@@ -720,43 +881,20 @@ mm_base_modem_sync_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
sync_ready (MMBaseModem *self,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
if (!MM_BASE_MODEM_GET_CLASS (self)->sync_finish (self, res, &error))
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return state_operation_finish (self, res, error);
}
void
mm_base_modem_sync (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
mm_base_modem_sync (MMBaseModem *self,
MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
if (!MM_BASE_MODEM_GET_CLASS (self)->sync ||
!MM_BASE_MODEM_GET_CLASS (self)->sync_finish) {
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
"Suspend/resume quick synchronization unsupported");
g_object_unref (task);
return;
}
MM_BASE_MODEM_GET_CLASS (self)->sync (self,
(GAsyncReadyCallback) sync_ready,
task);
state_operation (self,
STATE_OPERATION_TYPE_SYNC,
operation_lock,
callback,
user_data);
}
#endif /* WITH_SUSPEND_RESUME */
@@ -768,67 +906,70 @@ mm_base_modem_disable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return MM_BASE_MODEM_GET_CLASS (self)->disable_finish (self, res, error);
return state_operation_finish (self, res, error);
}
void
mm_base_modem_disable (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
mm_base_modem_disable (MMBaseModem *self,
MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable != NULL);
g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable_finish != NULL);
MM_BASE_MODEM_GET_CLASS (self)->disable (
self,
self->priv->cancellable,
callback,
user_data);
state_operation (self,
STATE_OPERATION_TYPE_DISABLE,
operation_lock,
callback,
user_data);
}
/******************************************************************************/
gboolean
mm_base_modem_enable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return MM_BASE_MODEM_GET_CLASS (self)->enable_finish (self, res, error);
return state_operation_finish (self, res, error);
}
void
mm_base_modem_enable (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
mm_base_modem_enable (MMBaseModem *self,
MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable != NULL);
g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable_finish != NULL);
MM_BASE_MODEM_GET_CLASS (self)->enable (
self,
self->priv->cancellable,
callback,
user_data);
state_operation (self,
STATE_OPERATION_TYPE_ENABLE,
operation_lock,
callback,
user_data);
}
/******************************************************************************/
gboolean
mm_base_modem_initialize_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return MM_BASE_MODEM_GET_CLASS (self)->initialize_finish (self, res, error);
return state_operation_finish (self, res, error);
}
void
mm_base_modem_initialize (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
mm_base_modem_initialize (MMBaseModem *self,
MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (MM_BASE_MODEM_GET_CLASS (self)->initialize != NULL);
g_assert (MM_BASE_MODEM_GET_CLASS (self)->initialize_finish != NULL);
MM_BASE_MODEM_GET_CLASS (self)->initialize (
self,
self->priv->cancellable,
callback,
user_data);
state_operation (self,
STATE_OPERATION_TYPE_INITIALIZE,
operation_lock,
callback,
user_data);
}
/******************************************************************************/
void
mm_base_modem_set_hotplugged (MMBaseModem *self,
gboolean hotplugged)