Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QML context object with JavaScriptOwnership garbage collected
Forum Updated to NodeBB v4.3 + New Features

QML context object with JavaScriptOwnership garbage collected

Scheduled Pinned Locked Moved QML and Qt Quick
7 Posts 4 Posters 4.1k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    bennofs
    wrote on last edited by bennofs
    #1

    The following program demonstrates the issue I'm having:

    class App : public QObject
    {
        Q_OBJECT
    public:
        virtual ~App() = default;
        Q_INVOKABLE bool f() const {
            return false;
        }
    };
    
    int main(int argc, char *argv[]) {
        QGuiApplication app(argc, argv);
        QQmlEngine engine;
    
        QObject* ctx = new App();
        QQmlEngine::setObjectOwnership(ctx, QQmlEngine::JavaScriptOwnership);
        engine.rootContext()->setContextObject(ctx);
    
        QQmlComponent component(&engine);
        QObject::connect(&component, &QQmlComponent::statusChanged, [&](auto status) {
            if(status == QQmlComponent::Ready) component.create(); 
            if(status == QQmlComponent::Error) {
              for(auto err : component.errors()) qCritical() << "Error: " << err.toString();
            }
        });
        component.loadUrl(QUrl::fromLocalFile("main.qml"));
    
        return app.exec();
    }
    

    With the QML document:

    import QtQuick 2.0
    import QtQuick.Window 2.2
    
    Window {
    
        Timer {
            interval: 1; running: true; repeat: true
            onTriggered: {
                console.log("call f");
                f(); gc();
                console.log("done");
            }
        }
    
    }
    

    If I run this, it outputs:

    $ ./main
    qml: call f
    qml: done
    qml: call f
    file:///data/libs/hsqml/member-undefined/main.qml:10: ReferenceError: f is not defined
    qml: call f
    fish: './main' terminated by signal SIGSEGV (Adressbereichsfehler)
    

    Is this expected behavior? When is it possible to use QQmlEngine::JavaScriptOwnership safely?


    I've already posted this on StackOverflow where it was suggested that I also create a post here.


    Since this example is from code in a library, I cannot just use setContextProperty (which appears to work), because that would break the library's interface.

    1 Reply Last reply
    0
    • N Offline
      N Offline
      Nathan H
      wrote on last edited by
      #2

      I'm not an expert on the QML runtime, but it seems like a Really Bad Idea (TM) to destroy a context object prior to the destruction of the context. If you need to create and destroy that object during the lifetime of the QML context, a much more natural implementation would be to have a C++ function create and return it, and call that function from javascript to obtain a handle on the object. Is there any reason you need it to be a context object?

      Furthermore, I would not advise calling gc() explicitly; if you're concerned about the exact lifetime of your objects, I recommend destroying them manually, not calling the garbage collector. Look at http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership for details on QObject ownership between JS and C++.

      Be well

      --
      Nathaniel

      1 Reply Last reply
      0
      • B Offline
        B Offline
        bennofs
        wrote on last edited by
        #3

        @Nathan-H I agree that destroying context objects prior to the destruction of the context is a Bad Idea, that's why I'm surprised that the Qt engine is doing this (there is no explict call in my code to destroy the object)! As for not calling gc() explictly, I agree, I only used gc() in the example to make the behavior of destroying the context object early happen more deterministically and showcase the problem better. In real code, the problem will occur much less frequent, non-deterministically. Because the GC may be invoked at any time, any program should not refuse to work when gc statements are inserted and this is a good test to make sure that the lifetimes of my objects are correct (which in this case, they clearly are not).

        1 Reply Last reply
        0
        • N Offline
          N Offline
          Nathan H
          wrote on last edited by
          #4

          OK, I see what you're saying now. So this could be a bug in Qt? But is it really necessary to have the object use Javascript ownership? Why not manage it's lifecycle directly in C++ by parenting it to the QQmlEngine or QGuiApplication or something?

          Be well

          --
          Nathaniel

          1 Reply Last reply
          0
          • B Offline
            B Offline
            bennofs
            wrote on last edited by
            #5

            Correct, for this particular case I could perhaps just use CppOwnership, but there are other places in the library where I definitely want JavaScriptOwnership (it's a binding to QML for the Haskell programming language, and you sometimes need "weak pointers" to objects that have been passed to QML). So I'd still be very interested in guidelines / specifics on what exactly I can expect from objects with JavaScriptOwnership.

            1 Reply Last reply
            0
            • C Offline
              C Offline
              chrisadams
              wrote on last edited by
              #6

              I suspect that there is no special handling for context objects, such that one with JavaScript ownership would be kept alive by the engine specifically.

              Unless I have misunderstood, in the test case you have posted, you are doing:

              1. expose context object (which has JS ownership)
              2. at time t0 resolve it within a JS context -- at this point the engine starts tracking it via references
              3. at time t1 drop the previous reference, and manually call gc -- at this point the engine will delete it

              This is expected behaviour. The object which you specifically declared to have JavaScript ownership, had its JS reference count drop from 1 to 0 and then the garbage collector was invoked.

              If you had set CppOwnership on it, then you would be saying "this object should be kept alive even after all JS references to it have been destroyed" - but you explicitly asked the engine to destroy it once all JS references had been destroyed, by setting JavaScriptOwnership.

              Or have I misunderstood your test case?

              1 Reply Last reply
              0
              • T Offline
                T Offline
                theonlylawislove
                wrote on last edited by
                #7

                I know this topic is old, but I'm wondering if any of the participants here made any progress with integrating the QML garbage collector with an external garbage collector (such as .NET)?

                Here is a SO question that will give some context: https://stackoverflow.com/questions/52471189/qt-using-setcontextproperty-with-qobject-and-javascriptownership-causes-i

                1 Reply Last reply
                0

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved