[gjs/ewlsh/implicit-mainloop: 8/8] Promise: Replace the cancellable (and its source) on reset




commit cb6fb83375fde8465f1362a26ee5c25d7d8c7917
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Tue Sep 28 20:10:58 2021 +0200

    Promise: Replace the cancellable (and its source) on reset
    
    We only need to reset a cancellable in case this has been cancelled
    previously, in such case it's better not to use its native reset
    function though, as that may lead to undefined behaviors.
    
    It's instead safer to replace the cancellable with a new one.

 gjs/promise.cpp | 37 +++++++++++++++++++++++++++----------
 1 file changed, 27 insertions(+), 10 deletions(-)
---
diff --git a/gjs/promise.cpp b/gjs/promise.cpp
index af62f94e..c017c939 100644
--- a/gjs/promise.cpp
+++ b/gjs/promise.cpp
@@ -44,6 +44,7 @@ class PromiseJobDispatcher::Source : public GSource {
     GjsAutoMainContext m_main_context;
     // The cancellable that stops this source.
     GjsAutoUnref<GCancellable> m_cancellable;
+    GjsAutoPointer<GSource, GSource, g_source_unref> m_cancellable_source;
 
     // G_PRIORITY_HIGH is -100, we set -1000 to ensure our source
     // always has the greatest priority. This means our prepare will
@@ -69,7 +70,9 @@ class PromiseJobDispatcher::Source : public GSource {
         g_source_set_ready_time(this, -1);
 
         // Drain the job queue.
-        m_gjs->runJobs(m_gjs->context(), m_cancellable);
+        GjsAutoUnref<GCancellable> cancellable(m_cancellable,
+                                               GjsAutoTakeOwnership{});
+        m_gjs->runJobs(m_gjs->context(), cancellable);
 
         return G_SOURCE_CONTINUE;
     }
@@ -85,7 +88,8 @@ class PromiseJobDispatcher::Source : public GSource {
     Source(GjsContextPrivate* gjs, GMainContext* main_context)
         : m_gjs(gjs),
           m_main_context(main_context, GjsAutoTakeOwnership()),
-          m_cancellable(g_cancellable_new()) {
+          m_cancellable(g_cancellable_new()),
+          m_cancellable_source(g_cancellable_source_new(m_cancellable)) {
         g_source_set_priority(this, PRIORITY);
 #if GLIB_CHECK_VERSION(2, 70, 0)
         g_source_set_static_name(this, "GjsPromiseJobQueueSource");
@@ -93,15 +97,13 @@ class PromiseJobDispatcher::Source : public GSource {
         g_source_set_name(this, "GjsPromiseJobQueueSource");
 #endif
 
-        GjsAutoPointer<GSource, GSource, g_source_unref> cancellable_source =
-            g_cancellable_source_new(m_cancellable);
         // Add our cancellable source to our main source,
         // this will trigger the main source if our cancellable
         // is cancelled.
-        g_source_add_child_source(this, cancellable_source);
+        g_source_add_child_source(this, m_cancellable_source);
         // Our cancellable source has no action, so set a dummy
         // callback.
-        g_source_set_dummy_callback(cancellable_source);
+        g_source_set_dummy_callback(m_cancellable_source);
     }
 
     void* operator new(size_t size) {
@@ -109,6 +111,8 @@ class PromiseJobDispatcher::Source : public GSource {
     }
     void operator delete(void* p) { g_source_unref(static_cast<GSource*>(p)); }
 
+    bool is_running() { return !!g_source_get_context(this); }
+
     /**
      * @brief Trigger the cancellable, detaching our source.
      */
@@ -118,7 +122,22 @@ class PromiseJobDispatcher::Source : public GSource {
      * overriding a previous cancel() call. Called by start() in
      * PromiseJobDispatcher to ensure the custom source will start.
      */
-    void reset() { g_cancellable_reset(m_cancellable); }
+    void reset() {
+        if (!g_cancellable_is_cancelled(m_cancellable))
+            return;
+
+        if (is_running())
+            g_source_remove_child_source(this, m_cancellable_source);
+        else
+            g_source_destroy(m_cancellable_source);
+
+        // Drop the old cancellable and create a new one, as per
+        // https://docs.gtk.org/gio/method.Cancellable.reset.html
+        m_cancellable = g_cancellable_new();
+        m_cancellable_source = g_cancellable_source_new(m_cancellable);
+        g_source_add_child_source(this, m_cancellable_source);
+        g_source_set_dummy_callback(m_cancellable_source);
+    }
 };
 
 GSourceFuncs PromiseJobDispatcher::Source::source_funcs = {
@@ -142,9 +161,7 @@ PromiseJobDispatcher::~PromiseJobDispatcher() {
     g_source_destroy(m_source.get());
 }
 
-bool PromiseJobDispatcher::is_running() {
-    return !!g_source_get_context(m_source.get());
-}
+bool PromiseJobDispatcher::is_running() { return m_source->is_running(); }
 
 void PromiseJobDispatcher::start() {
     // Reset the cancellable


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]