/* PIKA - Photo and Image Kooker Application * a rebranding of The GNU Image Manipulation Program (created with heckimp) * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio * * Original copyright, applying to most contents (license remains unchanged): * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * pikaasyncset.c * Copyright (C) 2018 Ell * * 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 3 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "core-types.h" #include "pikaasync.h" #include "pikaasyncset.h" #include "pikacancelable.h" #include "pikawaitable.h" enum { PROP_0, PROP_EMPTY }; struct _PikaAsyncSetPrivate { GHashTable *asyncs; }; /* local function prototypes */ static void pika_async_set_waitable_iface_init (PikaWaitableInterface *iface); static void pika_async_set_cancelable_iface_init (PikaCancelableInterface *iface); static void pika_async_set_dispose (GObject *object); static void pika_async_set_finalize (GObject *object); static void pika_async_set_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_async_set_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_async_set_wait (PikaWaitable *waitable); static gboolean pika_async_set_try_wait (PikaWaitable *waitable); static gboolean pika_async_set_wait_until (PikaWaitable *waitable, gint64 end_time); static void pika_async_set_cancel (PikaCancelable *cancelable); static void pika_async_set_async_callback (PikaAsync *async, PikaAsyncSet *async_set); G_DEFINE_TYPE_WITH_CODE (PikaAsyncSet, pika_async_set, G_TYPE_OBJECT, G_ADD_PRIVATE (PikaAsyncSet) G_IMPLEMENT_INTERFACE (PIKA_TYPE_WAITABLE, pika_async_set_waitable_iface_init) G_IMPLEMENT_INTERFACE (PIKA_TYPE_CANCELABLE, pika_async_set_cancelable_iface_init)) #define parent_class pika_async_set_parent_class /* private functions */ static void pika_async_set_class_init (PikaAsyncSetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = pika_async_set_dispose; object_class->finalize = pika_async_set_finalize; object_class->set_property = pika_async_set_set_property; object_class->get_property = pika_async_set_get_property; g_object_class_install_property (object_class, PROP_EMPTY, g_param_spec_boolean ("empty", NULL, NULL, FALSE, PIKA_PARAM_READABLE)); } static void pika_async_set_waitable_iface_init (PikaWaitableInterface *iface) { iface->wait = pika_async_set_wait; iface->try_wait = pika_async_set_try_wait; iface->wait_until = pika_async_set_wait_until; } static void pika_async_set_cancelable_iface_init (PikaCancelableInterface *iface) { iface->cancel = pika_async_set_cancel; } static void pika_async_set_init (PikaAsyncSet *async_set) { async_set->priv = pika_async_set_get_instance_private (async_set); async_set->priv->asyncs = g_hash_table_new (NULL, NULL); } static void pika_async_set_dispose (GObject *object) { PikaAsyncSet *async_set = PIKA_ASYNC_SET (object); pika_async_set_clear (async_set); G_OBJECT_CLASS (parent_class)->dispose (object); } static void pika_async_set_finalize (GObject *object) { PikaAsyncSet *async_set = PIKA_ASYNC_SET (object); g_hash_table_unref (async_set->priv->asyncs); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_async_set_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_async_set_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaAsyncSet *async_set = PIKA_ASYNC_SET (object); switch (property_id) { case PROP_EMPTY: g_value_set_boolean (value, pika_async_set_is_empty (async_set)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_async_set_wait (PikaWaitable *waitable) { PikaAsyncSet *async_set = PIKA_ASYNC_SET (waitable); while (! pika_async_set_is_empty (async_set)) { PikaAsync *async; GHashTableIter iter; g_hash_table_iter_init (&iter, async_set->priv->asyncs); g_hash_table_iter_next (&iter, (gpointer *) &async, NULL); pika_waitable_wait (PIKA_WAITABLE (async)); } } static gboolean pika_async_set_try_wait (PikaWaitable *waitable) { PikaAsyncSet *async_set = PIKA_ASYNC_SET (waitable); while (! pika_async_set_is_empty (async_set)) { PikaAsync *async; GHashTableIter iter; g_hash_table_iter_init (&iter, async_set->priv->asyncs); g_hash_table_iter_next (&iter, (gpointer *) &async, NULL); if (! pika_waitable_try_wait (PIKA_WAITABLE (async))) return FALSE; } return TRUE; } static gboolean pika_async_set_wait_until (PikaWaitable *waitable, gint64 end_time) { PikaAsyncSet *async_set = PIKA_ASYNC_SET (waitable); while (! pika_async_set_is_empty (async_set)) { PikaAsync *async; GHashTableIter iter; g_hash_table_iter_init (&iter, async_set->priv->asyncs); g_hash_table_iter_next (&iter, (gpointer *) &async, NULL); if (! pika_waitable_wait_until (PIKA_WAITABLE (async), end_time)) return FALSE; } return TRUE; } static void pika_async_set_cancel (PikaCancelable *cancelable) { PikaAsyncSet *async_set = PIKA_ASYNC_SET (cancelable); GList *list; list = g_hash_table_get_keys (async_set->priv->asyncs); g_list_foreach (list, (GFunc) g_object_ref, NULL); g_list_foreach (list, (GFunc) pika_cancelable_cancel, NULL); g_list_free_full (list, g_object_unref); } static void pika_async_set_async_callback (PikaAsync *async, PikaAsyncSet *async_set) { g_hash_table_remove (async_set->priv->asyncs, async); if (pika_async_set_is_empty (async_set)) g_object_notify (G_OBJECT (async_set), "empty"); } /* public functions */ PikaAsyncSet * pika_async_set_new (void) { return g_object_new (PIKA_TYPE_ASYNC_SET, NULL); } void pika_async_set_add (PikaAsyncSet *async_set, PikaAsync *async) { g_return_if_fail (PIKA_IS_ASYNC_SET (async_set)); g_return_if_fail (PIKA_IS_ASYNC (async)); if (g_hash_table_add (async_set->priv->asyncs, async)) { if (g_hash_table_size (async_set->priv->asyncs) == 1) g_object_notify (G_OBJECT (async_set), "empty"); pika_async_add_callback ( async, (PikaAsyncCallback) pika_async_set_async_callback, async_set); } } void pika_async_set_remove (PikaAsyncSet *async_set, PikaAsync *async) { g_return_if_fail (PIKA_IS_ASYNC_SET (async_set)); g_return_if_fail (PIKA_IS_ASYNC (async)); if (g_hash_table_remove (async_set->priv->asyncs, async)) { pika_async_remove_callback ( async, (PikaAsyncCallback) pika_async_set_async_callback, async_set); if (g_hash_table_size (async_set->priv->asyncs) == 0) g_object_notify (G_OBJECT (async_set), "empty"); } } void pika_async_set_clear (PikaAsyncSet *async_set) { PikaAsync *async; GHashTableIter iter; g_return_if_fail (PIKA_IS_ASYNC_SET (async_set)); if (pika_async_set_is_empty (async_set)) return; g_hash_table_iter_init (&iter, async_set->priv->asyncs); while (g_hash_table_iter_next (&iter, (gpointer *) &async, NULL)) { pika_async_remove_callback ( async, (PikaAsyncCallback) pika_async_set_async_callback, async_set); } g_hash_table_remove_all (async_set->priv->asyncs); g_object_notify (G_OBJECT (async_set), "empty"); } gboolean pika_async_set_is_empty (PikaAsyncSet *async_set) { g_return_val_if_fail (PIKA_IS_ASYNC_SET (async_set), FALSE); return g_hash_table_size (async_set->priv->asyncs) == 0; }