Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QMetaObject::invokeMethod doesn't work when...
Forum Update on Monday, May 27th 2025

QMetaObject::invokeMethod doesn't work when...

Scheduled Pinned Locked Moved General and Desktop
5 Posts 2 Posters 7.9k Views
  • 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.
  • D Offline
    D Offline
    Davita
    wrote on last edited by
    #1

    ... called from a static class and non-main thread.
    In short, I have a class "sapp", which has another static class "tobj" as a static member. To avoid static order initialization fiasco, tobj is declared inside sapp's method, which in turn, returns pointer of tobj's instance.
    My problem is that, tobj has a timer which should be started in the constructor, and tobj may be created by non-main thread. QTimer can't be started by a thread other than main thread (or the one which doesn't have event loop i guess).
    for that reason, I invoke QTimer::start via QMetaObject::invokeMethod + Qt::QueuedConnection to avoid thread problem, however it doesn't work, QTimer::start is never invoked. I investigated a bit the problem and looks like, QTimer::start is not invoked because QTimer's parent(tobj in this case) is declared as static. If I declare tobj as a non static member, everything works fine.

    I don't understand quite well the internals of Qt, could this be a bug or I'm doing something wrong?

    here's the code:

    @class tobj : public QObject
    {
    Q_OBJECT

    QTimer timer;
    

    private slots:
    void timeout();

    public:
    tobj();
    };

    class sapp : public QObject
    {
    Q_OBJECT

    public:
    static tobj* f();
    };@

    @void tobj::timeout()
    {
    qDebug() << "hi";
    }

    tobj::tobj()
    {
    connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
    timer.setInterval(500);
    qDebug() << QMetaObject::invokeMethod(&timer, "start", Qt::QueuedConnection); // returns true, but never invoked.
    }

    tobj* sapp::f()
    {
    static tobj ff;
    return &ff;
    }
    @

    Here's a link to the test project, consisting of 1 header and 1 cpp file http://dl.dropbox.com/u/3055964/untitled.zip

    I'm testing on Qt 4.8.0 and MSVC 2010.

    Thank you very much, your help is much appreciated.

    1 Reply Last reply
    0
    • A Offline
      A Offline
      alexisdm
      wrote on last edited by
      #2

      The timer is created in the QtConcurrent::run thread, so it will only timeout if that thread has an event loop running, and it isn't the case, using Qt::QueuedConnection doesn't change that.

      If you want to create the unique tobj object as soon as a sapp object is created, you can simply use an helper class:

      @class tobjHelper {
      static tobj* instance;
      public:
      tobjHelper() {
      // This is not thread safe at all
      if (!instance) {
      // Because the QTimer needs an event loop
      Q_ASSERT_X(QCoreApplication::instance(), Q_FUNC_INFO, "a QCoreApplication or QApplication object has to be instantiated");
      instance = new tobj();
      }
      }

       // not static to prevent returning NULL
       tobj *f() const {
           return instance;
       }
      

      };

      // In the cpp:
      // tobj* tobjHelper::instance = 0;

      class sapp : public QObject, tobjHelper
      {
      Q_OBJECT
      };@
      Because sapp inherits from tobjHelper, its default constructor will be called, and that constructor will create a tobj if one doesn't already exist. And sapp::f() will returns that tobj instance.

      1 Reply Last reply
      0
      • D Offline
        D Offline
        Davita
        wrote on last edited by
        #3

        @alexisdm, thank you very much for you assistance. I think it's more clear now. So to be clear, timeout never triggered because QTimer wasn't created in the thread which had an event loop right?
        and the purpose of f() method is to make sure that instance member is always initialized?

        And lastly, in your case, isn't it possible that sapp's constructor to be called from non-main thread? in this case what happens?

        Thank you very much again :)

        1 Reply Last reply
        0
        • A Offline
          A Offline
          alexisdm
          wrote on last edited by
          #4

          [quote author="Davita" date="1335597866"]So to be clear, timeout never triggered because QTimer wasn't created in the thread which had an event loop right? [/quote]Yes.

          [quote]and the purpose of f() method is to make sure that instance member is always initialized?[/quote]Yes, the purpose of f() not being static is so that a tobjHelper has already been created when we call the function, which means the instance member was also initialized.
          [quote]And lastly, in your case, isn't it possible that sapp's constructor to be called from non-main thread? in this case what happens?[/quote]With my previous code, if the constructor is called from another thread, the QTimer will also belongs to that thread, and if it doesn't have an event loop, the timeout() signal won't be fired.
          You could solve that with something like:
          @tobj::tobj()
          : timer(this) // make the timer a child object of tobj
          {
          // move this and its children to the main thread
          moveToThread(QCoreApplication::instance()->thread());

          connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
          timer.setInterval(500);
          QMetaObject::invokeMethod(&timer, "start", Qt::QueuedConnection);
          

          }@

          But the instance creation is still not thread safe: if 2 sapp object are created in 2 different threads at the same time, they can both find that the instance member is NULL and each initialize it (the exact same problem(s) that you would have to initialize a singleton in a thread safe way, since it is basically a singleton pattern).

          1 Reply Last reply
          0
          • D Offline
            D Offline
            Davita
            wrote on last edited by
            #5

            Thank you very much alexisdm, actually I already tried moveToThread, but it doesn't work in my case, I don't know why. I also checked that tobj didn't have parent, but still, no result. I ended up initializing static stuff outside the class itself, it's ugly but it works. I will try your suggestion soon and let you know the results :)

            Thanks again

            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