/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details: * * Copyright 2025 Dan Williams */ #include #include "mm-utils.h" #include "mm-sleep-context.h" static void log_object_iface_init (MMLogObjectInterface *iface); G_DEFINE_TYPE_EXTENDED (MMSleepContext, mm_sleep_context, G_TYPE_OBJECT, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init)) struct _MMSleepContextPrivate { GTask *task; guint timeout_id; }; enum { DONE, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; /*****************************************************************************/ static gchar * log_object_build_id (MMLogObject *_self) { return g_strdup_printf ("sleep-context"); } /*****************************************************************************/ static void timeout_cleanup (MMSleepContext *self) { if (self->priv->timeout_id) g_source_remove (self->priv->timeout_id); self->priv->timeout_id = 0; } void mm_sleep_context_complete (MMSleepContext *self, GError *error) { timeout_cleanup (self); if (error) { mm_obj_dbg (self, "completing with error '%s'", error->message); g_task_return_error (self->priv->task, error); } else { mm_obj_dbg (self, "completing with success"); g_task_return_boolean (self->priv->task, TRUE); } g_clear_object (&self->priv->task); } static gboolean operation_timeout (MMSleepContext *self) { mm_obj_dbg (self, "timeout"); mm_sleep_context_complete (self, g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "timeout waiting for sleep preparation to complete")); return G_SOURCE_REMOVE; } static void operation_ready (MMSleepContext *self, GAsyncResult *res) { g_autoptr(GError) error = NULL; gboolean success; mm_obj_dbg (self, "notifying listeners task is done"); success = g_task_propagate_boolean (G_TASK (res), &error); g_assert (success || error); g_signal_emit (self, signals[DONE], 0, error); } /*****************************************************************************/ MMSleepContext * mm_sleep_context_new (guint timeout_seconds) { MMSleepContext *self; self = MM_SLEEP_CONTEXT (g_object_new (MM_TYPE_SLEEP_CONTEXT, NULL)); self->priv->task = g_task_new (self, NULL, (GAsyncReadyCallback)operation_ready, NULL); self->priv->timeout_id = g_timeout_add_seconds (timeout_seconds, (GSourceFunc)operation_timeout, self); mm_obj_dbg (self, "new context with %d second timeout", timeout_seconds); return self; } static void log_object_iface_init (MMLogObjectInterface *iface) { iface->build_id = log_object_build_id; } static void mm_sleep_context_init (MMSleepContext *self) { /* Initialize opaque pointer to private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SLEEP_CONTEXT, MMSleepContextPrivate); } static void dispose (GObject *object) { MMSleepContext *self = MM_SLEEP_CONTEXT (object); timeout_cleanup (self); /* Task must always be completed and cleared before disposal */ g_assert (self->priv->task == NULL); G_OBJECT_CLASS (mm_sleep_context_parent_class)->dispose (object); } static void mm_sleep_context_class_init (MMSleepContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMSleepContextPrivate)); /* Virtual methods */ object_class->dispose = dispose; signals[DONE] = g_signal_new (MM_SLEEP_CONTEXT_DONE, G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (MMSleepContextClass, done), NULL, /* accumulator */ NULL, /* accumulator data */ g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_ERROR); }