[gjs/gnome-40: 15/30] object: Safely handle disposal when happening from other thread




commit 760012f80ce30a7a96e044fa5f6f981587a52cdd
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Mon Mar 29 04:16:26 2021 +0200

    object: Safely handle disposal when happening from other thread
    
    In case we dispose an object from another thread we still queue a toggle
    down event to happen in an idle, so that it can be picked by main
    thread at next iteration.
    
    However, in such case we'll have no more the instance set on the object
    private data. We still keep track of it through the disposed data
    though, so we can use it to trigger a toggle down event.
    
    It's an error if we get an invalid ("true") pointer here, so we log
    about it.
    
    (cherry-picked from commit c4fc8213)

 gi/object.cpp                                      | 19 +++++++++++++++++--
 installed-tests/js/testGObjectDestructionAccess.js | 16 ++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index e18620ec..072c46c2 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -1257,12 +1257,27 @@ static void
 toggle_handler(GObject               *gobj,
                ToggleQueue::Direction direction)
 {
+    auto* self = ObjectInstance::for_gobject(gobj);
+
+    if (G_UNLIKELY(!self)) {
+        void* disposed = g_object_get_qdata(gobj, ObjectBase::disposed_quark());
+
+        if (G_UNLIKELY(disposed == gjs_int_to_pointer(DISPOSED_OBJECT))) {
+            g_critical("Handling toggle %s for an unknown object %p",
+                       direction == ToggleQueue::UP ? "up" : "down", gobj);
+            return;
+        }
+
+        // In this case the object has been disposed but its wrapper not yet
+        self = static_cast<ObjectInstance*>(disposed);
+    }
+
     switch (direction) {
         case ToggleQueue::UP:
-            ObjectInstance::for_gobject(gobj)->toggle_up();
+            self->toggle_up();
             break;
         case ToggleQueue::DOWN:
-            ObjectInstance::for_gobject(gobj)->toggle_down();
+            self->toggle_down();
             break;
         default:
             g_assert_not_reached();
diff --git a/installed-tests/js/testGObjectDestructionAccess.js 
b/installed-tests/js/testGObjectDestructionAccess.js
index 6504d844..b5523804 100644
--- a/installed-tests/js/testGObjectDestructionAccess.js
+++ b/installed-tests/js/testGObjectDestructionAccess.js
@@ -290,4 +290,20 @@ describe('Disposed or finalized GObject', function () {
         GjsTestTools.unref_other_thread(file);
         file.run_dispose();
     });
+
+    it('can be disposed from other thread', function () {
+        let file = Gio.File.new_for_path('/');
+        file.expandMeWithToggleRef = true;
+        file.ref();
+        GjsTestTools.unref_other_thread(file);
+        GjsTestTools.run_dispose_other_thread(file);
+    });
+
+    it('can be garbage collected once disposed from other thread', function () {
+        let file = Gio.File.new_for_path('/');
+        file.expandMeWithToggleRef = true;
+        GjsTestTools.run_dispose_other_thread(file);
+        file = null;
+        System.gc();
+    });
 });


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