Patrick Jeeves
Patrick Jeeves

Reputation: 391

How to manually send a QSignal from multiple std::threads?

Essentially, I'm trying to add a Qt GUI onto an already existing GLUT application I made, and I'm not sure how to pass signals from the existing code into Qt, without introducing the overhead of making dozens of existing classes QObjects. Currently I'm using a QTimer and an event loop, but that isn't scaling very well, seeming to involve a lot of waiting for mutexes to then decide there's no work, with attempts to resolve this more increasing coupling than fixing it.

The best alternate I could come up with is this:

static std::mutex relay_mutex;
static std::condition_variable relay_condition;
static std::queue<std::pair<int, void*> > relay_queue;

extern "C"
void passMessage(int a, void * b)
{
    std::lock_guard<std::mutex> lock(relay_mutex);
    relay_queue.emplace_back(a, b);
    relay_condition.notify_all();
}

class RelayThread : public QThread
{
    Q_OBJECT

public:
    RelayThread() {}

signals:
    void passMessage(int, void *);

protected:
    void run() override
    {
        std::unique_lock<std::mutex> lock(relay_mutex);

        for(;;)
        {
            while(relay_queue.empty())
                relay_condition.wait();

            do {
                emit passMessage(relay_queue.front().first, relay_queue.front().second);

                if(relay_queue.front().first == 0)
                    return;

                relay_queue.pop()
            } while(relay_queue.size());
        }
    }
};

But this will obviously incur the overhead of an extra context switch, which is no good either. Is there a way to manually queue up a signal without going through a QThread like this?

Upvotes: 0

Views: 119

Answers (1)

Laszlo
Laszlo

Reputation: 801

This should be sufficient. You can let Qt do the queueing, if needed, by setting the connection type to Qt::QueuedConnection, the value of the third argument of the connect() call.

#include <QObject>

void passMessage(int a, MessageType b) {
   Relay r(a,b);
}

class Relay: public QObject {
    Q_OBJECT
public:
    Relay(int a, MessageType b) {
      emit passMessage(a, b);
    }
signals:
    void passMessage(int, MessageType);
};

You will also need to register a metatype for MessageType. See here.

Upvotes: 1

Related Questions