PDA

View Full Version : Socket thread affinity problem, with code



Anslem
1st June 2014, 16:10
I am developing a simple program, a sever\client file manager. I use threads in that program and i ran into this problem.



// this class is responsible for making connection with a server and sending it a command
class CommandSender : public QObject
{
Q_OBJECT
public:
...
QTcpSocket* socketToServer;
QTcpSocket* connectToServer(const Command* command);
...
}


1)"connectToServer" establishes a conenction with a server, sends it a command that was passed as a parameter and returns
a connected socket ready to recieve a reply from the server.
2) Command class is a base class for all commands that a client can send to a server, for exeample - DownloadCommand. Basically, all command classes
have a funtion "QString GetCommandString()", not important here.
3) Every command class has a Runnable analog, i.e there is DownloadCommand and there is DownloadCommandRunnable. For QThreadPool usage.

Now, this is a main GUI thread function:


void MainWindow::on_actionDownload_triggered()
{
...
DownloadCommandRunnable *command = new DownloadCommandRunnable();
threadPool->start(command);
}




void DownloadCommandRunnable::run()
{
DownloadCommand command;

CommandSender sender;
// socketToReceiveFile is a DownloadCommandRunnable class' member field
socketToReceiveFile = sender.loadServerFileHierarchy(&command);

connect(this, SIGNAL(readyToWork()), this, SLOT(start()));

emit readyToWork();
// start an event loop
}

void DownloadCommandRunnable::start()
{
socketToReceiveFile->write("ready to receive file"); // this is the point of interest
}


When i send that string to a server i get this warning:
QSocketNotifier: socket notifiers cannot be enabled from another thread

I searched the internet for an answer and it seems like this message appears when socket is created in one thread and being used in another.

Thing is, "socketToReceiveFile" is a member field of CommandSender, that is created in the "run" function. That means that i'm trying to use the "write" function in the thread where "socketToReceiveFile" was created.

I got rid of that message using this code:



void DownloadCommandRunnable::start()
{
QTcpSocket* socket = new QTcpSocket();
socket->setSocketDescriptor(socketToReceiveFile->socketDescriptor());
socket->write("ready to receive file");
}


But i can't understand why it works like that. Both "socketToReceiveFile" and "socket" are created in the same thread, but one gives me an error and the other works just fine.

anda_skoa
2nd June 2014, 07:56
Thing is, "socketToReceiveFile" is a member field of CommandSender, that is created in the "run" function. That means that i'm trying to use the "write" function in the thread where "socketToReceiveFile" was created.

No. You are calling write in a slot. You connection is of type AutoConnection, the slot will be called in the context of the thread owning the DownloadCommandRunnable instance, most likely your main thread.
If you call start() directly, then you are calling write in the context of the threadpool thread.

Cheers,
_

Anslem
2nd June 2014, 11:51
anda_skoa thanks, i see it now. Can i move the runnable object to the thread it's running in? I can't do smth like:



QThread *currentThread = QThread::currentThread();
this->moveToThread(currentThread);

anda_skoa
2nd June 2014, 18:21
No, an object can only be move to another thread by the thread currently owning the object.

One thing you can do is treat the run() method like main(), i.e. create the receiver object as well and connect to it.

Cheers,
_