Discussion:
[Interest] Best way to invoke a slot after some time with QObject::sender
Preet
2012-08-28 02:10:48 UTC
Permalink
Hiya,

I have a project where I'm trying to asynchronously reply to a signal.
So if ObjectA sends ObjectB a signal, ObjectB will send ObjectA a
reply signal at some later point in time. ObjectA is guaranteed to be
alive when ObjectB decides to send a reply. ObjectAs and ObjectB also
live in different QThreads. ObjectB doesn't know about ObjectA in
advance, so I use QObject::sender() to store ObjectA's pointer in a
queue for when ObjectB is ready to reply. There are many such
"ObjectA"s out there that ObjectB can reply to.

I see three ways to implement this in an application:

When ObjectB is ready to reply,
1 have it connect to the right ObjectA, emit a signal, then disconnect.
2 use QMetaObject::invoke() to invoke the right ObjectA's corresponding slot
3 be connected to all ObjectA's and just emit a signal with the
QObject sender pointer; this way the ObjectAs can filter out which
signals are for them in the slot itself

I'd like to know what the best method is in terms of speed/minimal
complexity, or if there's a better method I haven't listed. ObjectB
may be required to send hundreds of replies every second, so speed is
important. 3 seems like its the worst, since every reply will invoke
multiple slots. 1 and 2 seem similar; I'm kind of concerned about
calling "connect" and "disconnect" so often in 1.


Regards,

Preet
Bo Thorsen
2012-08-28 06:25:44 UTC
Permalink
Post by Preet
Hiya,
I have a project where I'm trying to asynchronously reply to a signal.
So if ObjectA sends ObjectB a signal, ObjectB will send ObjectA a
reply signal at some later point in time. ObjectA is guaranteed to be
alive when ObjectB decides to send a reply. ObjectAs and ObjectB also
live in different QThreads. ObjectB doesn't know about ObjectA in
advance, so I use QObject::sender() to store ObjectA's pointer in a
queue for when ObjectB is ready to reply. There are many such
"ObjectA"s out there that ObjectB can reply to.
When ObjectB is ready to reply,
1 have it connect to the right ObjectA, emit a signal, then disconnect.
2 use QMetaObject::invoke() to invoke the right ObjectA's corresponding slot
3 be connected to all ObjectA's and just emit a signal with the
QObject sender pointer; this way the ObjectAs can filter out which
signals are for them in the slot itself
I'd like to know what the best method is in terms of speed/minimal
complexity, or if there's a better method I haven't listed. ObjectB
may be required to send hundreds of replies every second, so speed is
important. 3 seems like its the worst, since every reply will invoke
multiple slots. 1 and 2 seem similar; I'm kind of concerned about
calling "connect" and "disconnect" so often in 1.
The third is the worst, if you have a bunch of ObjectA's. If you only
have a couple of them, this is by far the best. Because this also means
you don't have to worry about whether the object has been deleted since
it emitted the signal.

The first actually uses the second, because those are cross thread
signal slot connections. But by using the connect, you will
automatically get the call moved across to the other thread. If you use
invoke, you don't get this. If it's okay to call across threads, invoke
is the fastest.

You have to figure out how to make sure the ObjectA still exists at the
point where ObjectB wants to call it. This is the part that really
worries me with the situation you have described here.

Instead of doing this, I'd probably implement some kind of multiplexer
object that sits between the two threads and handles all the
communication and knows the lifetime of both ObjectA and ObjectB.
Whether you would want one big object that handles all of them, or
create a connection object for each connect, that's up to you.

Bo Thorsen,
Fionia Software.
--
Expert Qt and C++ developer for hire
Contact me if you need expert Qt help
http://www.fioniasoftware.dk
Preet
2012-08-28 08:08:43 UTC
Permalink
Hi Bo,
Post by Bo Thorsen
The first actually uses the second, because those are cross thread
signal slot connections. But by using the connect, you will
automatically get the call moved across to the other thread. If you use
invoke, you don't get this. If it's okay to call across threads, invoke
is the fastest.
It seems like invokeMethod() uses Qt::AutoConnection by default...
doesn't this mean that I'd be okay (I don't have any return args --
the desired behaviour would be that invokeMethod() post an event in
ObjectA's event loop)?
Post by Bo Thorsen
You have to figure out how to make sure the ObjectA still exists at the
point where ObjectB wants to call it. This is the part that really
worries me with the situation you have described here.
All ObjectAs are 'eternal'. They're actually QtPlugins that are alive
for the life of the application.
BRM
2012-08-28 13:20:23 UTC
Permalink
Post by Bo Thorsen
Post by Preet
Hiya,
I have a project where I'm trying to asynchronously reply to a signal.
So if ObjectA sends ObjectB a signal, ObjectB will send ObjectA a
reply signal at some later point in time. ObjectA is guaranteed to be
alive when ObjectB decides to send a reply.  ObjectAs and ObjectB also
live in different QThreads. ObjectB doesn't know about ObjectA in
advance, so I use QObject::sender() to store ObjectA's pointer in a
queue for when ObjectB is ready to reply. There are many such
"ObjectA"s out there that ObjectB can reply to.
When ObjectB is ready to reply,
1  have it connect to the right ObjectA, emit a signal, then disconnect.
2  use QMetaObject::invoke() to invoke the right ObjectA's corresponding slot
3  be connected to all ObjectA's and just emit a signal with the
QObject sender pointer; this way the ObjectAs can filter out which
signals are for them in the slot itself
I'd like to know what the best method is in terms of speed/minimal
complexity, or if there's a better method I haven't listed. ObjectB
may be required to send hundreds of replies every second, so speed is
important. 3 seems like its the worst, since every reply will invoke
multiple slots. 1 and 2 seem similar; I'm kind of concerned about
calling "connect" and "disconnect" so often in 1.
The third is the worst, if you have a bunch of ObjectA's. If you only
have a couple of them, this is by far the best. Because this also means
you don't have to worry about whether the object has been deleted since
it emitted the signal.
The first actually uses the second, because those are cross thread
signal slot connections. But by using the connect, you will
automatically get the call moved across to the other thread. If you use
invoke, you don't get this. If it's okay to call across threads, invoke
is the fastest.
You have to figure out how to make sure the ObjectA still exists at the
point where ObjectB wants to call it. This is the part that really
worries me with the situation you have described here.
Instead of doing this, I'd probably implement some kind of multiplexer
object that sits between the two threads and handles all the
communication and knows the lifetime of both ObjectA and ObjectB.
Whether you would want one big object that handles all of them, or
create a connection object for each connect, that's up to you.
I had a similar situation where I had an inverse of QSignalMapper - I had a signal with a QByteArray for data and a uint32_t "key".
Only receives with the same "key" were to get the data. The senders, multiplexer, and receives were all in their own threads.

Now, the nice thing about #2 is that QMetaMethod::invoke()'s second argument[1] is the same as the last argument in QObject::connect().
Would it not be wrong to say that that handles the movement across the threads for you then? It's worked very well for me at least.

http://doc.qt.nokia.com/4.7-snapshot/qmetamethod.html#invoke

Ben

Till Oliver Knoll
2012-08-28 06:36:19 UTC
Permalink
Post by Preet
Hiya,
I have a project where I'm trying to asynchronously reply to a signal.
So if ObjectA sends ObjectB a signal, ObjectB will send ObjectA a
reply signal at some later point in time.
That sounds like "broken by design" to me: you'd have a dependency circle here! In order to *connect* to a signal you have to *know* the signal emiter's class. So signals should only go one way: bottom up.

That said, your problem sounds a little bit what QNetworkAccessManager is solving: you have multiple clients that request (by method call!) something from the network, they get a handle (object) for the time being (with which they can also e.g. abort the current operation), the QNAM does its work asynchronously and finally emits a signal (actually several ones in each state transition) once it's done (it doesn't know its clients though!).

Why wouldn't that approach work in your case?

Cheers,
Oliver
André Somers
2012-08-28 07:21:37 UTC
Permalink
Post by Till Oliver Knoll
Post by Preet
Hiya,
I have a project where I'm trying to asynchronously reply to a signal.
So if ObjectA sends ObjectB a signal, ObjectB will send ObjectA a
reply signal at some later point in time.
That sounds like "broken by design" to me: you'd have a dependency circle here! In order to *connect* to a signal you have to *know* the signal emiter's class. So signals should only go one way: bottom up.
That said, your problem sounds a little bit what QNetworkAccessManager is solving: you have multiple clients that request (by method call!) something from the network, they get a handle (object) for the time being (with which they can also e.g. abort the current operation), the QNAM does its work asynchronously and finally emits a signal (actually several ones in each state transition) once it's done (it doesn't know its clients though!).
Why wouldn't that approach work in your case?
QFuture and QFutureWatcher are also designed for such tasks, but
unfortunately it only works with the Qt Concurrent framework..

André
Preet
2012-08-28 08:22:51 UTC
Permalink
Post by Till Oliver Knoll
Post by Preet
I have a project where I'm trying to asynchronously reply to a signal.
So if ObjectA sends ObjectB a signal, ObjectB will send ObjectA a
reply signal at some later point in time.
That sounds like "broken by design" to me: you'd have a dependency circle here! In order to *connect* to a signal you have to *know* the signal emiter's class. So signals should only go one way: bottom up.
Yeah, it's a little broken :) All the objects that are talking to
each other in my scenario are actually plugins. I couldn't come up
with a way to tell a potential plugin dev how to 'talk' to my ObjectB
plugin without directly saying "give your plugin a slot called
"onSomeSignal()" if you want me to talk to you!"
Post by Till Oliver Knoll
That said, your problem sounds a little bit what QNetworkAccessManager is solving: you have multiple clients that request (by method call!) something from the network, they get a handle (object) for the time being (with which they can also e.g. abort the current operation), the QNAM does its work asynchronously and finally emits a signal (actually several ones in each state transition) once it's done (it doesn't know its clients though!).
Why wouldn't that approach work in your case?
I like this idea a lot... its a bit more work, but at least it'd let
me adhere to the observer pattern. I'm gonna try and implement it,
thanks for the idea!
Till Oliver Knoll
2012-08-28 09:35:37 UTC
Permalink
Post by Preet
I couldn't come up
with a way to tell a potential plugin dev how to 'talk' to my ObjectB
plugin without directly saying "give your plugin a slot called
"onSomeSignal()" if you want me to talk to you!"
That's easy: simply *make* the plugins implement a given slot - enforce it! ;)

The tool of choice here is called "interface". C++ doesn't exactly support the notion of "interfaces" such as Java or Objective-C ("protocols") or any other languages which have this feature built in.

But you can easily create the same behaviour with C++ "pure abstract classes" (I guess you must have already have something like this in place anyway).

class MyPluginInterface : public QObject
{
Q_OBJECT
public:
virtual void doFoo() = 0; // plugins MUST implement this

public slots:
void handleBar(Bar *someBar) = 0; // haven't actually tried setting a slot as pure virtual, but should work

...
};

(On Windows: don't forget to "DLL export" that interface - Qt offers some nice macros for that in a platform independent manner...)

Off course the above is not exactly "pure", as it inherits from QObject and the Q_OBJECT macro expands to more code etc. But it serves its purpose to make plugins implement the desired methods and slots.

Then have some "Controller" class ("Plugin Manager") connect the signals from your ObjectB to the slots (which now are guaranteed to be there) of the plugins (when they are loaded, when they become "active" or any other suitable time in their lifecycle).


OR (my preferred way):

Don't enforce that slot and simply tell your plugins: "If you want me to talk to you, connect YOURSELF! Here's the ObjectB signal you're interested in."

That way the plugin is more flexible, because the plugin's logic might know better WHEN to connect (and disconnect).


Also I mentiomed a "handle" object in my previous post. If you want to avoid that every single plugin is informed by ObjectB when something happened ("result is ready, pick it up!" - Plugin: "Oh nice! But wait... the result is not for me - I have a different handle object..."), you could have the handle object emit the signal(s) instead of the ObjectB (which would trigger the signal emission on that particular handle).

So only the plugin having that particular handle would get informed by that signal. Also only the plugin(s) actually waiting for a result would be informed, as the others would have released their handle objects already (and hence be disconnected from any signals).

Makes sense?

Cheers,
Oliver
Loading...