[gnome-builder/wip/gtk4-port: 50/94] threading: add ide_cancellable_chain()
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port: 50/94] threading: add ide_cancellable_chain()
- Date: Mon, 28 Mar 2022 20:27:08 +0000 (UTC)
commit d8714734e7c6e6f8d72ad716b4ee652506dfe5ed
Author: Christian Hergert <chergert redhat com>
Date: Fri Sep 24 13:20:30 2021 -0700
threading: add ide_cancellable_chain()
src/libide/threading/ide-cancellable.c | 187 ++++++++++++++++++++++++++++++++
src/libide/threading/ide-cancellable.h | 29 +++++
src/libide/threading/libide-threading.h | 1 +
src/libide/threading/meson.build | 2 +
4 files changed, 219 insertions(+)
---
diff --git a/src/libide/threading/ide-cancellable.c b/src/libide/threading/ide-cancellable.c
new file mode 100644
index 000000000..d58a9eac8
--- /dev/null
+++ b/src/libide/threading/ide-cancellable.c
@@ -0,0 +1,187 @@
+/* ide-cancellable.c
+ *
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-cancellable"
+
+#include "config.h"
+
+#include "ide-cancellable.h"
+
+#define CHAINED_INFO_MAGIC 0x81734637
+
+typedef struct
+{
+ guint magic;
+ volatile gint ref_count;
+ GMutex mutex;
+ GWeakRef self;
+ GWeakRef other;
+ gulong other_handler;
+} ChainedInfo;
+
+static void
+chained_info_free (gpointer data)
+{
+ ChainedInfo *info = data;
+ g_autoptr(GCancellable) self = NULL;
+ g_autoptr(GCancellable) other = NULL;
+
+ g_assert (info != NULL);
+ g_assert (info->magic == CHAINED_INFO_MAGIC);
+ g_assert (info->ref_count == 0);
+
+ info->magic = 0;
+
+ self = g_weak_ref_get (&info->self);
+ other = g_weak_ref_get (&info->other);
+
+ if (other != NULL && info->other_handler != 0)
+ g_clear_signal_handler (&info->other_handler, other);
+ else
+ info->other_handler = 0;
+
+ g_weak_ref_clear (&info->other);
+ g_weak_ref_clear (&info->self);
+
+ g_mutex_clear (&info->mutex);
+
+ g_slice_free (ChainedInfo, info);
+}
+
+static void
+chained_info_unref (ChainedInfo *info)
+{
+ g_autoptr(GCancellable) other = NULL;
+
+ g_assert (info != NULL);
+ g_assert (info->ref_count > 0);
+ g_assert (info->magic == CHAINED_INFO_MAGIC);
+
+ if ((other = g_weak_ref_get (&info->other)))
+ {
+ gulong handler_id;
+
+ g_mutex_lock (&info->mutex);
+ handler_id = info->other_handler;
+ info->other_handler = 0;
+ g_mutex_unlock (&info->mutex);
+
+ if (handler_id)
+ g_signal_handler_disconnect (other, handler_id);
+ }
+
+ if (g_atomic_int_dec_and_test (&info->ref_count))
+ chained_info_free (info);
+}
+
+static void
+ide_cancellable_cancelled_cb (GCancellable *other,
+ ChainedInfo *info)
+{
+ g_autoptr(GCancellable) self = NULL;
+
+ g_assert (G_IS_CANCELLABLE (other));
+ g_assert (info != NULL);
+ g_assert (info->ref_count > 0);
+ g_assert (info->magic == CHAINED_INFO_MAGIC);
+
+ self = g_weak_ref_get (&info->self);
+
+ if (self != NULL)
+ {
+ if (!g_cancellable_is_cancelled (self))
+ g_cancellable_cancel (self);
+ }
+
+ g_clear_signal_handler (&info->other_handler, other);
+}
+
+static void
+ide_cancellable_weak_cb (gpointer data,
+ GObject *where_object_was)
+{
+ ChainedInfo *info = data;
+
+ g_assert (info != NULL);
+ g_assert (info->ref_count > 0);
+ g_assert (info->magic == CHAINED_INFO_MAGIC);
+
+ chained_info_unref (info);
+}
+
+/**
+ * ide_cancellable_chain:
+ * @self: (nullable): a #GCancellable or %NULL
+ * @other: (nullable): a #GCancellable or %NULL
+ *
+ * If both @self and @other are not %NULL, then the cancellation of
+ * @other will be propagated to @self if @other is cancelled.
+ *
+ * If @self and @other are the same, @self is returned and no additional
+ * chaining will occur.
+ *
+ * If @self and @other are %NULL, then %NULL is returned.
+ * If @self is non-%NULL, it will be returned.
+ * If @self is %NULL and @other is non-%NULL, other will be
+ * returned. This is useful to succinctly chain cancellables like:
+ *
+ * |[
+ * cancellable = ide_cancellable_chain (cancellable, self->cancellable);
+ * ]|
+ *
+ * Returns: (transfer none) (nullable): a #GCancellable or %NULL
+ *
+ * Since: 3.28
+ */
+GCancellable *
+ide_cancellable_chain (GCancellable *self,
+ GCancellable *other)
+{
+ ChainedInfo *info;
+
+ g_return_val_if_fail (!self || G_IS_CANCELLABLE (self), NULL);
+ g_return_val_if_fail (!other || G_IS_CANCELLABLE (other), NULL);
+
+ if (self == other)
+ return self;
+ else if (self == NULL)
+ return other;
+ else if (other == NULL)
+ return self;
+
+ /*
+ * We very much want to avoid taking a reference in the process
+ * here because that makes it difficult to know if we've created
+ * any sort of reference cycles or cancellable leaks.
+ */
+
+ info = g_slice_new0 (ChainedInfo);
+ info->magic = CHAINED_INFO_MAGIC;
+ info->ref_count = 3;
+ g_mutex_init (&info->mutex);
+ g_weak_ref_init (&info->self, self);
+ g_weak_ref_init (&info->other, other);
+ g_object_weak_ref (G_OBJECT (self), ide_cancellable_weak_cb, info);
+ g_object_weak_ref (G_OBJECT (other), ide_cancellable_weak_cb, info);
+ info->other_handler = g_cancellable_connect (other,
+ G_CALLBACK (ide_cancellable_cancelled_cb),
+ info,
+ (GDestroyNotify)chained_info_unref);
+
+ return self;
+}
diff --git a/src/libide/threading/ide-cancellable.h b/src/libide/threading/ide-cancellable.h
new file mode 100644
index 000000000..b00a9e751
--- /dev/null
+++ b/src/libide/threading/ide-cancellable.h
@@ -0,0 +1,29 @@
+/* ide-cancellable.h
+ *
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+IDE_AVAILABLE_IN_ALL
+GCancellable *ide_cancellable_chain (GCancellable *self,
+ GCancellable *other);
+
+G_END_DECLS
diff --git a/src/libide/threading/libide-threading.h b/src/libide/threading/libide-threading.h
index b321f06ab..c085137f6 100644
--- a/src/libide/threading/libide-threading.h
+++ b/src/libide/threading/libide-threading.h
@@ -24,6 +24,7 @@
#define IDE_THREADING_INSIDE
+#include "ide-cancellable.h"
#include "ide-environment.h"
#include "ide-environment-variable.h"
#include "ide-subprocess-launcher.h"
diff --git a/src/libide/threading/meson.build b/src/libide/threading/meson.build
index d628be2ff..c9e9e99cd 100644
--- a/src/libide/threading/meson.build
+++ b/src/libide/threading/meson.build
@@ -6,6 +6,7 @@ libide_include_directories += include_directories('.')
#
libide_threading_public_headers = [
+ 'ide-cancellable.h',
'ide-environment.h',
'ide-environment-variable.h',
'ide-subprocess.h',
@@ -35,6 +36,7 @@ libide_threading_private_sources = [
]
libide_threading_public_sources = [
+ 'ide-cancellable.c',
'ide-environment-variable.c',
'ide-environment.c',
'ide-gtask.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]