PDA

View Full Version : QSocketNotifier: socket notifiers cannot be disabled from another thread



dabiabilus
19th March 2007, 12:00
I have a code like this

void Client::run()
{

tcpSocket = new QTcpSocket();

if (tcpSocket->setSocketDescriptor(socketDesc))
{

peerAdress = tcpSocket->peerAddress().toString();
peerPort = tcpSocket->peerPort();

emit connected();
}

while (!quit)
{
if (tcpSocket->waitForReadyRead(Timeout))
{
// Read data
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_4_0);
in >> buffer;
qDebug() << buffer<<endl;
}


}
//slot to send a message
void Client::sendMessage(QString message)
{
tcpSocket->write(message);
}


I have also sender thread gets message from message queues and send signal to thread above to write message socket.

it is implemented as below.

void Sender::run()
{

while(!quit)
{
qDebug() <<"sending message" <<endl;
emit sendMessage("message");

sleep(1);
}
}

but when code runs I get following error.

what is the problem.


QObject: Cannot create children for a parent that is in a different thread.
and
QSocketNotifier: socket notifiers cannot be disabled from another thread

danadam
19th March 2007, 14:03
I have a code like this
If it is a code, why don't you use
tag?


[code]void Client::run()
{
tcpSocket = new QTcpSocket();
I assume that Client class is derived from QThread class.Parent object (Client) was probably created in the main thread. Everything that is done in its run() method is executed in another thread. So child object (QTcpSocket) is created in different thread than its parent lives. This is not allowed and that's why you get error:

QObject: Cannot create children for a parent that is in a different thread.

QSocketNotifier: socket notifiers cannot be disabled from another thread
Hm... I'll be guessing here. Your client thread doesn't have an event loop (you're not calling exec() method anywhere in run()). You are waiting for readyRead in client thread, so in fact you are waiting for some signal from QTcpSocket. But it's impossible to receive it in client thread cause it has no event loop.

jpn
19th March 2007, 14:18
What is buffer? Where has it been created?

dabiabilus
19th March 2007, 16:26
If it is a code, why don't you use [code] tag?


I assume that Client class is derived from QThread class.Parent object (Client) was probably created in the main thread. Everything that is done in its run() method is executed in another thread. So child object (QTcpSocket) is created in different thread than its parent lives. This is not allowed and that's why you get error:

yes that is true , but where should I create QTcpSocket, in the constructor of the Client or elsewhere. Also in QT documentation it is stated that

"The child of a QObject must always be created in the thread where the parent was created. This implies, among other things, that you should never pass the QThread object (this) as the parent of an object created in the thread (since the QThread object itself was created in another thread).
"
is this means that child of a QObject(tcpSocket) should be created in the main thread. Or do not passing "this"(Client) into constructor of the tcpSocket in the run method will be sufficent


Hm... I'll be guessing here. Your client thread doesn't have an event loop (you're not calling exec() method anywhere in run()). You are waiting for readyRead in client thread, so in fact you are waiting for some signal from QTcpSocket. But it's impossible to receive it in client thread cause it has no event loop.


Why do I need an evet loop. tcpSocket can signal events such as "connected","disconnected" ,"errors". I do not need to call exec. I will try it.
Problem occurs when Client receives "sendMessage" signal of the sender, and tries to write socket.

danadam
19th March 2007, 21:26
Why do I need an evet loop. tcpSocket can signal events such as "connected","disconnected" ,"errors". I do not need to call exec. I will try it.
My mistake here, event loop isn't required. But I'm not sure if you are aware that in such case your sendMessage slot isn't executed in client thread. There are three threads here: main thread, client thread and sender thread. Socket reading is done in client thread, that's obvious. But sendMessage slot is executed either in main thread (if you make queued connection between Sender and Client) or in sender thread (if you make direct connection between Sender and Client).

Problem occurs when Client receives "sendMessage" signal of the sender, and tries to write socket.
I wrote a program like yours (two threads, Client and Sender) and I don't have that problem. Of course "QObject: Cannot create children for a parent that is in a different thread." is still there, but I wasn't able to reproduce "QSocketNotifier: socket notifiers cannot be disabled from another thread"

jacek
19th March 2007, 22:30
My mistake here, event loop isn't required.
On the contrary, it is required to make the queued connections work.


sendMessage slot is executed either in main thread (if you make queued connection between Sender and Client) or in sender thread (if you make direct connection between Sender and Client).
Most likely it's executed in the Sender thread. QThread objects live in the thread that has created them, so most likely both Sender and Client objects live in the main thread, while the socket lives in the Client thread.

Since both Sender and Client live in the same thread, most likely a direct connection was made between them, whereas it should be a queued connection. When making connections between thread object, it's safer to explicitly specify the connection type.

danadam
19th March 2007, 23:14
To make things clear, here is output from my test program (three threads: main, client, sender, neither client nor sender has event loop, client and sender live in main thread, socket lives in client thread):

Qt::AutoConnection (slot is executed in main thread):


"main, thread: b70a28e0"
server is listening
incoming connection
"client started, thread: b6e82bb0"
"sender started, thread: b6681bb0"
"sender emit signal, thread: b6681bb0"
"client slot, thread: b70a28e0"
QObject: Cannot create children for a parent that is in a different thread.
"sender emit signal, thread: b6681bb0"
"client slot, thread: b70a28e0"


Qt::DirectConnection (slot is executed in sender thread):


"main, thread: b71698e0"
server is listening
incoming connection
"client started, thread: b6f49bb0"
"sender started, thread: b6748bb0"
"sender emit signal, thread: b6748bb0"
"client slot, thread: b6748bb0"
QObject: Cannot create children for a parent that is in a different thread.
"sender emit signal, thread: b6748bb0"
"client slot, thread: b6748bb0"


Qt::QueuedConnection (slot is executed in main thread):


"main, thread: b70df8e0"
server is listening
incoming connection
"client started, thread: b6ebfbb0"
"sender started, thread: b66bebb0"
"sender emit signal, thread: b66bebb0"
"client slot, thread: b70df8e0"
QObject: Cannot create children for a parent that is in a different thread.
"sender emit signal, thread: b66bebb0"
"client slot, thread: b70df8e0"

jacek
19th March 2007, 23:59
Qt::AutoConnection (slot is executed in main thread):
That's quite interesting. The docs say:
If the signal is emitted from the thread in which the receiving object lives, the slot is invoked directly, as with Qt::DirectConnection; otherwise the signal is queued, as with Qt::QueuedConnection.
So it looks like that if you use Qt::AutoConnection, the effective connection type is established for each signal, not once when the connection is created.


Qt::QueuedConnection (slot is executed in main thread):
It looks like the Client object lives in the main thread, so the meta call events go through main thread's event queue. One possible solution is to use QObject::moveToThread() (of course in such case there must be an event loop running in the Client thread).

dabiabilus
20th March 2007, 08:54
That's quite interesting. The docs say:
So it looks like that if you use Qt::AutoConnection, the effective connection type is established for each signal, not once when the connection is created.


It looks like the Client object lives in the main thread, so the meta call events go through main thread's event queue. One possible solution is to use QObject::moveToThread() (of course in such case there must be an event loop running in the Client thread).



I solve the problem as below, but there is other things to solve.

Sender thread lives in main thread.
Client lives main thread.
Socket lives in Client thread.

As you have said , with direct connection between sender and client ,Client slot is invoked in sender thread. That is why, slot can not write to socket due to fact that socket lives in Client thread.

With queued connection between sender and client , slot of Client is invoked in main thread because client lives in main thread. That is why, client slot can not write socket due to fact that socket lives in Client thread not in main thread.

To solve the problem, I have created new class that is responsible for initialization of the socket. This class also have slot that writes to the socket. I have created this class in the run block of the Client class. And connect to signal of the sender with slot of this class. it worked correctly using queud connection but as you said I needed "exec" call to be able use queued connection.

But when I call "exec" it blocks in the run block. I can not do anything inside. for example I can not use blocking approach as follows.

while (!quit)
{

if (tcpSocket->waitForReadyRead(Timeout))
{
// Read data
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_4_0);
in >> buffer;
qDebug() << buffer<<endl;

}
}


because I have to read and write same socket in my design I have to alternative

1 -) Do not use blocking approach. Use simply signal / slot way. but as I know signals are slow . my application time critical. should handle lots of messages.I can not choose this approach.

2-) use blocking approach but create separe sockets for writing and reading.

3-) Use an other socket library

am I right ?

danadam
20th March 2007, 11:19
As you have said , with direct connection between sender and client ,Client slot is invoked in sender thread. That is why, slot can not write to socket due to fact that socket lives in Client thread.

With queued connection between sender and client , slot of Client is invoked in main thread because client lives in main thread. That is why, client slot can not write socket due to fact that socket lives in Client thread not in main thread.

To say the truth I don't know why you can't write to socket in different thread. My previous guessing was only... er... guessing. I checked that and it appears that there is no problem with writing to socket in sender thread or main thread. Client slot in the above examples did it and it wasn't complaining.

jacek
20th March 2007, 22:19
1 -) Do not use blocking approach. Use simply signal / slot way. but as I know signals are slow
What exactly is slow in signals?


am I right ?
There is also a 4th solution: you can use a non-blocking approach without using signals and slots.