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




commit cfde537805c609f733edbb2683556e1f250ccfcc
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, 26 insertions(+), 11 deletions(-)
---
diff --git a/gjs/promise.cpp b/gjs/promise.cpp
index 64010ef4..2f074613 100644
--- a/gjs/promise.cpp
+++ b/gjs/promise.cpp
@@ -25,6 +25,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
@@ -50,7 +51,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;
     }
@@ -66,7 +69,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");
@@ -74,10 +78,8 @@ class PromiseJobDispatcher::Source : public GSource {
         g_source_set_name(this, "GjsPromiseJobQueueSource");
 #endif
 
-        GSource* cancellable_source = g_cancellable_source_new(m_cancellable);
-        g_source_add_child_source(this, cancellable_source);
-        g_source_set_dummy_callback(cancellable_source);
-        g_source_unref(cancellable_source);
+        g_source_add_child_source(this, m_cancellable_source);
+        g_source_set_dummy_callback(m_cancellable_source);
     }
 
     void* operator new(size_t size) {
@@ -85,14 +87,29 @@ 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.
      */
     void cancel() { g_cancellable_cancel(m_cancellable); }
     /**
-     * @brief Reset the cancellable
+     * @brief Reset the cancellable if it has been previously cancelled
      */
-    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);
+
+        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 = {
@@ -116,9 +133,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]