Trolltech Home | Qt4-preview-feedback Home | Recent Threads | All Threads | Author | Date
All threads index page 3

Qt4-preview-feedback Archive, January 2005
Signals across threads and QThread as parent for QObjects

Pages: Prev | 1 | 2 | Next

Message 1 in thread

The documentation says, "...you must ensure that all objects created in 
a thread are deleted before you delete the QThread. This can be done 
easily by passing the QThread object as the parent for objects that 
otherwise wouldn't have a parent."

However, when I do that, I get the following error...

ASSERT failure in QApplication::sendEvent: "Cannot send events to 
objects owned by a different thread (bfe76fa0). Receiver '' (of type 
'CheckerThread') was created in thread 0", file kernel/qapplication.cpp, 
line 2626
Aborted

If I do not pass parents to the objects created in the thread, it works 
fine.  What kind of n00b mistake am I making?  Here is the source...

// CheckerThread.h
#ifndef _CHECKER_THREAD_
#define _CHECKER_THREAD_

#include <QThread>

class TextChecker : public QObject
{
		Q_OBJECT
	
	public:
		TextChecker(QObject *parent = 0);

	public slots:
		void getLatestText();

};


class CheckerThread : public QThread
{
		Q_OBJECT
		
	public:
		CheckerThread(QObject *parent = 0);

	protected:
		void run();

};

#endif



// CheckerThread.cpp
#include "CheckerThread.h"
#include <QTimer>
#include <iostream>

TextChecker::TextChecker(QObject *parent)
: QObject(parent)
{ }

void TextChecker::getLatestText()
{
	std::cout << "foo" << std::endl;
}


CheckerThread::CheckerThread(QObject *parent)
: QThread(parent)
{ }

void CheckerThread::run()
{
	// Removing 'this' fixes the problem.
	TextChecker *tc = new TextChecker(this);

	// Removing 'this' fixes the problem.
	QTimer *timer = new QTimer(this);

	connect(timer, SIGNAL(timeout()), tc, SLOT(getLatestText()));
	timer->start(5000);

	exec();
}



// main.cpp
#include <QApplication>
#include <QPushButton>

#include "CheckerThread.h"

int main(int argc, char *argv[])
{
	QApplication app(argc, argv);


	CheckerThread ct;
	ct.start();


	QPushButton hello("Hello world!");

	app.setMainWidget(&hello);
	hello.show();
	return app.exec();
}


Message 2 in thread

Jeff Meininger wrote:

> The documentation says, "...you must ensure that all objects created in 
> a thread are deleted before you delete the QThread. This can be done 
> easily by passing the QThread object as the parent for objects that 
> otherwise wouldn't have a parent."

I'm still experimenting with this.  Please check out my original post:
http://lists.trolltech.com/qt4-preview-feedback/2005-01/msg00300.html

First of all, if I compile as 'release' instead of 'debug', then 
obviously the ASSERT error goes away and I can test what's really happening.

If I pass 'this' as the parent to objects created in the CheckerThread's 
run(), then they get deleted when the thread gets deleted (as one would 
expect).  But this means that they get deleted by code executing in the 
thread that created the CheckerThread... not by the CheckerThread thread 
itself.

Maybe the documentation means that I should pass 'this' only to objects 
created while constructing my CheckerThread (and not objects created in 
run()?)  In other words, objects created in CheckerThread's constructor 
can have CheckerThread as a parent, but NOT objects created in 
CheckerThread::run()... right?

I understand how everything works if I pass no parent objects to 
QObjects created in run(), and simply delete them in run() after exec() 
returns.  The documentation makes me think that I should be taking 
advantage of a more elegant built-in memory management system, though.

What's the scoop on this?  What's the correct way to do it??


Message 3 in thread

Jeff Meininger wrote:
> The documentation says, "...you must ensure that all objects created in 
> a thread are deleted before you delete the QThread. This can be done 
> easily by passing the QThread object as the parent for objects that 
> otherwise wouldn't have a parent."

This is actually an error in the documentation.  A QThread instance 
"lives" in the thread that created the instance, NOT in the thread it 
creates.  I will talk to the doc team to get this fixed.  The best 
advice I can offer it to create your objects on the stack in your run() 
implementaiton, and allow the compiler to clean things up for you.

> However, when I do that, I get the following error...
> 
> ASSERT failure in QApplication::sendEvent: "Cannot send events to 
> objects owned by a different thread (bfe76fa0). Receiver '' (of type 
> 'CheckerThread') was created in thread 0", file kernel/qapplication.cpp, 
> line 2626
> Aborted
> 
> If I do not pass parents to the objects created in the thread, it works 
> fine.  What kind of n00b mistake am I making?  Here is the source...
[snip]
-- 
 [ signature omitted ] 

Message 4 in thread

On Monday 17 January 2005 02:18 am, Bradley T Hughes wrote:
> This is actually an error in the documentation.  A QThread instance
> "lives" in the thread that created the instance, NOT in the thread it
> creates.  I will talk to the doc team to get this fixed.  The best
> advice I can offer it to create your objects on the stack in your run()
> implementaiton, and allow the compiler to clean things up for you.

I noticed in the blockingfortuneclient example, the FortuneThread object has 
signals declared, but then these signals are called from within the run() 
function.  How or why is this safe?

-Justin


Message 5 in thread

Justin Karneges wrote:
> On Monday 17 January 2005 02:18 am, Bradley T Hughes wrote:
> 
>> This is actually an error in the documentation.  A QThread instance
>> "lives" in the thread that created the instance, NOT in the thread
>> it creates.  I will talk to the doc team to get this fixed.  The 
>> best advice I can offer it to create your objects on the stack in 
>> your run() implementaiton, and allow the compiler to clean things 
>> up for you.
> 
> I noticed in the blockingfortuneclient example, the FortuneThread 
> object has signals declared, but then these signals are called from 
> within the run() function.  How or why is this safe?

Emitting the newFortune() and error() signals from run() in
FortuneThread is safe for a couple of reasons:

1. Signal emission is thread safe.  You can emit multiple signals from
the same object in several threads without needing any
synchronization.  Qt does this for you.  Also, in theory,
doing so will also result in concurrent emission in true parallel
environments (but we haven't verified this).

2. When emitting a signal for an object that lives in a thread other
than the current thread (i.e. 'emit obj->signal()' where 'obj->thread()
!= QThread::currentThread()'), we use QThread::currentThread() to
do the magic auto-connection-type detection instead of the object's
thread.

These features were added specifically to allow QThread subclasses to
emit signals (e.g. QThread::finished() as well as ones defined in
subclasses) without the need for locking.

Hope this answers your questions.  Andreas was just in my office, and we 
both agreed that perhaps this kind of detailed information about the 
internals would be useful in the documentation (i.e. we'll see about 
putting this in the docs). :)

> -Justin

-- 
 [ signature omitted ] 

Message 6 in thread

On Monday 17 January 2005 03:48 am, Bradley T Hughes wrote:
> 2. When emitting a signal for an object that lives in a thread other
> than the current thread (i.e. 'emit obj->signal()' where 'obj->thread()
> != QThread::currentThread()'), we use QThread::currentThread() to
> do the magic auto-connection-type detection instead of the object's
> thread.

So this means that if the emit occurs in a different thread than the object 
lives in, the signal emit is always queued, regardless of what the original 
connect() mode was set to?

If the answer is yes, then this raises another question: what is the point of 
being able to set the mode in connect(), when it can be overridden at the 
time of emit?  Maybe the last argument to connect() should be axed.

-Justin


Message 7 in thread

Justin Karneges wrote:
> On Monday 17 January 2005 03:48 am, Bradley T Hughes wrote:
> 
>> 2. When emitting a signal for an object that lives in a thread
>> other than the current thread (i.e. 'emit obj->signal()' where
>> 'obj->thread() != QThread::currentThread()'), we use
>> QThread::currentThread() to do the magic auto-connection-type
>> detection instead of the object's thread.
> 
> So this means that if the emit occurs in a different thread than the
> object lives in, the signal emit is always queued, regardless of what
> the original connect() mode was set to?

The signal is always queued, iff the connection type is 
Qt::AutoConnection (the default).  More below...

> If the answer is yes, then this raises another question: what is the
> point of being able to set the mode in connect(), when it can be
> overridden at the time of emit?  Maybe the last argument to connect()
> should be axed.

Specifying the connection type works.  What I describe above is only for 
Qt::AutoConnection.  If you specify Qt::DirectConnection when 
connecting, then the slot will always be activated immediately (per the 
DirectConnection rules), regardless of the current thread, the object's 
thread or the receivers thread.  Same with Qt::QueuedConnection, if you 
specify this connection type, ALL emissions are queued, regardless of 
thread, object or receiver.

> -Justin
> 
> -- List archive and information:
> http://lists.trolltech.com/qt4-preview-feedback/
> 


-- 
 [ signature omitted ] 

Message 8 in thread

On Wednesday 19 January 2005 03:32 am, Bradley T Hughes wrote:
> Specifying the connection type works.  What I describe above is only for
> Qt::AutoConnection.  If you specify Qt::DirectConnection when
> connecting, then the slot will always be activated immediately (per the
> DirectConnection rules), regardless of the current thread, the object's
> thread or the receivers thread.  Same with Qt::QueuedConnection, if you
> specify this connection type, ALL emissions are queued, regardless of
> thread, object or receiver.

Oh, interesting.  But is this actually useful?  The way I see it, setting the 
mode to Direct when you need Queued can only result in disaster, and setting 
the mode to Queued when Direct would have worked is only good for debugging 
Qt.  Why even allow the user a choice?

-Justin


Message 9 in thread

Justin Karneges wrote:
> On Wednesday 19 January 2005 03:32 am, Bradley T Hughes wrote:
> 
>> Specifying the connection type works.  What I describe above is
>> only for Qt::AutoConnection.  If you specify Qt::DirectConnection
>> when connecting, then the slot will always be activated immediately
>> (per the DirectConnection rules), regardless of the current thread,
>> the object's thread or the receivers thread.  Same with
>> Qt::QueuedConnection, if you specify this connection type, ALL
>> emissions are queued, regardless of thread, object or receiver.
> 
> Oh, interesting.  But is this actually useful?  The way I see it,
> setting the mode to Direct when you need Queued can only result in
> disaster, and setting the mode to Queued when Direct would have
> worked is only good for debugging Qt.  Why even allow the user a
> choice?

I think it's very useful.  "Using Direct when you need Queued, or Queued 
when Direct works"... well, all I can say is "pilot error".  If you need 
one, don't use the other :)

Even with Qt::AutoConnection, allowing the user to make an explicit 
choice makes sense.  For example, if you write thread-safe slots, why 
not use Direct?  For widgets that need updating (i.e. repainting) when 
something changes, and many things tend to change at once, why not use 
Queued (allowing the system to compress the events into one final event)?

> -Justin
> 
> -- List archive and information:
> http://lists.trolltech.com/qt4-preview-feedback/

-- 
 [ signature omitted ] 

Message 10 in thread

On Wednesday 19 January 2005 11:37, Bradley T Hughes wrote:
>  (allowing the system to compress the events into one final event)?

What? Multiple emissions or the same signal to the same slot are compressed 
into one by the event loop? Hopefully not! :)

Marc

-- 
 [ signature omitted ] 

Message 11 in thread

Hi,

> What? Multiple emissions or the same signal to the same slot are compressed 
> into one by the event loop? Hopefully not! :)

I understand events are compressed, not signals. Event compression has 
always been there.

--
 [ signature omitted ] 

Message 12 in thread

On Wednesday 19 January 2005 20:54, Dimitri wrote:
> Hi,
>
> > What? Multiple emissions or the same signal to the same slot are
> > compressed into one by the event loop? Hopefully not! :)
>
> I understand events are compressed, not signals. Event compression has
> always been there.
<snip>

I did understand that, too. I was just disoriented by Bradley's comment:

> For widgets that need updating (i.e. repainting) when 
> something changes, and many things tend to change at once, why not use 
> Queued (allowing the system to compress the events into one final event)?

Marc

-- 
 [ signature omitted ] 

Message 13 in thread

On Wednesday 19 January 2005 21:04, Marc Mutz wrote:
> On Wednesday 19 January 2005 20:54, Dimitri wrote:
> > Hi,
> >
> > > What? Multiple emissions or the same signal to the same slot are
> > > compressed into one by the event loop? Hopefully not! :)
> >
> > I understand events are compressed, not signals. Event compression
> > has always been there.
>
> <snip>
>
> I did understand that, too. I was just disoriented by Bradley's 
comment:

Maybe he was talking about anything like QListView::triggerUpdate()? Not 
Qt does the compression but you've to code it yourself.


André


Message 14 in thread

Marc Mutz wrote:
> On Wednesday 19 January 2005 11:37, Bradley T Hughes wrote:
>> (allowing the system to compress the events into one final event)?
> 
> What? Multiple emissions or the same signal to the same slot are
> compressed into one by the event loop? Hopefully not! :)

Of course not... I was a bit unclear:

- multiple queued emissions to QWidget::update() (which posts a paint event)
- the system will compress all those paint events into one.

That should explain what I was thinking ;)

> Marc

-- 
 [ signature omitted ] 

Message 15 in thread

On Wednesday 19 January 2005 04:37 am, Bradley T Hughes wrote:
> Justin Karneges wrote:
> > Oh, interesting.  But is this actually useful?  The way I see it,
> > setting the mode to Direct when you need Queued can only result in
> > disaster, and setting the mode to Queued when Direct would have
> > worked is only good for debugging Qt.  Why even allow the user a
> > choice?
>
> I think it's very useful.  "Using Direct when you need Queued, or Queued
> when Direct works"... well, all I can say is "pilot error".  If you need
> one, don't use the other :)
>
> Even with Qt::AutoConnection, allowing the user to make an explicit
> choice makes sense.  For example, if you write thread-safe slots, why
> not use Direct?  For widgets that need updating (i.e. repainting) when
> something changes, and many things tend to change at once, why not use
> Queued (allowing the system to compress the events into one final event)?

Wow, I guess.  There seems to be a lot of things you can do here.

I have a wish then, and it is one I've asked for in the past: the ability for 
the emitter to defer a signal using clean code (as opposed to using 
qInvokeMetaMember).  I only mention this again, because maybe after all these 
major changes to QObject you've done, it might be easier now.  After all, 
you've got code now that decides the method based on some thread ids.  How 
about opening up this decision to the user?

  emitLater fooSignal(some, nice, args);

-Justin


Pages: Prev | 1 | 2 | Next