diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index 2ace30984..c41b5e68c 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -2055,3 +2055,70 @@ _nm_utils_unescape_spaces (char *str) } #undef IS_SPACE + +/*****************************************************************************/ + +typedef struct { + gpointer callback_user_data; + GCancellable *cancellable; + NMUtilsInvokeOnIdleCallback callback; + gulong cancelled_id; + guint idle_id; +} InvokeOnIdleData; + +static gboolean +_nm_utils_invoke_on_idle_cb_idle (gpointer user_data) +{ + InvokeOnIdleData *data = user_data; + + data->idle_id = 0; + nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); + + data->callback (data->callback_user_data, data->cancellable); + nm_g_object_unref (data->cancellable); + g_slice_free (InvokeOnIdleData, data); + return G_SOURCE_REMOVE; +} + +static void +_nm_utils_invoke_on_idle_cb_cancelled (GCancellable *cancellable, + InvokeOnIdleData *data) +{ + /* on cancellation, we invoke the callback synchronously. */ + nm_clear_g_signal_handler (data->cancellable, &data->cancelled_id); + nm_clear_g_source (&data->idle_id); + data->callback (data->callback_user_data, data->cancellable); + nm_g_object_unref (data->cancellable); + g_slice_free (InvokeOnIdleData, data); +} + +void +nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data, + GCancellable *cancellable) +{ + InvokeOnIdleData *data; + + g_return_if_fail (callback); + + data = g_slice_new (InvokeOnIdleData); + data->callback = callback; + data->callback_user_data = callback_user_data; + data->cancellable = nm_g_object_ref (cancellable); + if ( cancellable + && !g_cancellable_is_cancelled (cancellable)) { + /* if we are passed a non-cancelled cancellable, we register to the "cancelled" + * signal an invoke the callback synchronously (from the signal handler). + * + * We don't do that, + * - if the cancellable is already cancelled (because we don't want to invoke + * the callback synchronously from the caller). + * - if we have no cancellable at hand. */ + data->cancelled_id = g_signal_connect (cancellable, + "cancelled", + G_CALLBACK (_nm_utils_invoke_on_idle_cb_cancelled), + data); + } else + data->cancelled_id = 0; + data->idle_id = g_idle_add (_nm_utils_invoke_on_idle_cb_idle, data); +} diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index fb37535d7..7ee76799b 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -909,4 +909,13 @@ void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...); const char *_nm_utils_escape_spaces (const char *str, char **to_free); char *_nm_utils_unescape_spaces (char *str); +/*****************************************************************************/ + +typedef void (*NMUtilsInvokeOnIdleCallback) (gpointer callback_user_data, + GCancellable *cancellable); + +void nm_utils_invoke_on_idle (NMUtilsInvokeOnIdleCallback callback, + gpointer callback_user_data, + GCancellable *cancellable); + #endif /* __NM_SHARED_UTILS_H__ */