0
votes

I have a condition where I have unknown amount of 3rd party threads calling a callback in my application. That callback emits a signal in the context of the threads that called it. Always the same signal, but 10 different threads can emit it at any given moment.

I'd like to queue all of those singlas and process them with the appropriate slot in the context of a single QThread I own.

How do I do that? The following code does not work. Although I see it signals being emitted, from different threads, my "On..." is never called.

QObject::connect(this,SIGNAL(ProcessQueuedOutEvent(int)),
        this,
        SLOT(OnProcessQueuedOutEvent(int)),
        Qt::QueuedConnection);
2
Meh - does 'OutEvent' take an auxiliary pointer/int that is returned in the callback, and so can be used to transfer context? - Martin James
Oh I would definitely have to copy the buffers to my slot. I assume whatever pointer structures I get will be erased once the function returns. - JasonGenX
Is your class "this" a QThread object, what do you mean by owning the QThread ? - Kirell
yes. it "is" a QThread. - JasonGenX
Inheriting from QThread?! If you not re-implementing how Qt handles threads, you're doing it wrong: blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong - TheDarkKnight

2 Answers

0
votes

Does your QThread run the event loop? It has to do it to receive signals:

Queued Connection The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.

Basically queued connection works the following way:

  1. The originator issues a signal.
  2. Qt creates an event and posts it into the receiver event queue.
  3. The receiver goes through its event queue, picks up the events and dispatches the signals into the connected slots.

Hence if you do not run the event queue, the signals are posted but your thread will never receive them.

So basically your thread should do some initialization in run() but then call exec() and pass it to Qt.

If your thread also needs to run some periodic operations besides checking for signals, you can do that by using QTimer::singleShot timers posting signals to the same thread.

See http://qt-project.org/doc/qt-4.8/threads-qobject.html#signals-and-slots-across-threads

PS. If you pass the pointers via queued connections, the pointer must be valid until the signal is processed, which may be after your function which posted the signal existed. A common error is to post signals with strings as a parameters which are stored in a local char[] buffer. At the moment the buffer is accessed the original function is finished, and the string is already gone. Those errors depend on thread scheduling and therefore hard to debug. If you pass the pointers via queued connection, they must be heap-allocated and the callee must be responsible to free them.

0
votes

If I understand your problem correctly, you have a callback function executed by many threads. This callback function should emit a signal connected to a slot in a object which is in another thread.

What I suggest is to create a threaded receiver object, using the pattern (moveToThread). Then using the postEvent method to a private implementation method. The call is thread safe (the parameter is copied).

So your callbacks can directly and safely call:

OnProcessQueuedOutEvent 

which posts an event to the QThread event loop.

Receiver.h

class Receiver : public QObject
{
    Q_OBJECT
public:
    explicit Receiver( QObject* parent = 0 );
    virtual ~Receiver();

public slots:
    void OnProcessQueuedOutEvent( int val );

private slots:
    void OnProcessQueuedOutEventImpl( int val );

private:
    QThread m_thread;
};

Receiver.cpp

Receiver::Receiver( QObject* parent )
    : QObject(parent)
{
    moveToThread(&m_thread);
    m_thread.start();
}

Receiver::~Receiver()
{
    // Gracefull thread termination (queued in exec loop)
    if( m_thread.isRunning() ) {
        m_thread.quit();
        m_thread.wait();
    }
}

void OnProcessQueuedOutEvent( int val )
{
    QMetaObject::invokeMethod(this, "OnProcessQueuedOutEventImpl", Q_ARG(int,val));
}

void OnProcessQueuedOutEventImpl( int val )
{
    // do stuff here
}