0

I have multithread server (inherits QTcpServer). When new connection appears, I create new task (inherits QRunnable), passing socket descriptor to constructor and push this task to QThreadpool (have 3 workers).

QThreadPool::globalInstance()->start(task);

In run() I dynamically create QTcpSocket, set socket descriptor and read first received byte. Based on value of this byte I create new specific task (also inherits QRunnable), passing to its ctr pointer to earlier created QTcpSocket object, and also push this task to QThreadpool.

This specific task make some routine and app crashes. From log file, I see destructor of this specific task was called.

Also Qt Creator throws next error message:

QObject: Cannot create children for a parent that is in a different thread. (Parent is QNativeSocketEngine(0x18c62290), parent's thread is QThread(0x18c603e0), current thread is QThread(0x18cc3b60) QSocketNotifier: socket notifiers cannot be disabled from another thread ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 18cc3b60. Receiver '' (of type 'QNativeSocketEngine') was created in thread 18c603e0", file kernel/qcoreapplication.cpp, line 420

I found similar posts but unfortunately I could not understand how to fix my problem. Please, help me.

Daniel Hedberg
  • 5,677
  • 4
  • 36
  • 61

1 Answers1

1

You cannot use QTcpSocket from two different threads, because QObjects are not thread-safe.

You've created your QTcpSocket in the first task, so it "lives" in the thread associated with that task. If you pass its pointer into another QRunnable, then a second thread will try to access it, which will break things.

You'll need to redesign your app in a way that doesn't share the same QTcpSocket between different threads. One possibility is to implement different specific functions in your original task, and simply select the appropriate function based on the first received byte

JKSH
  • 2,658
  • 15
  • 33
  • Thanks for the help. Before posting issue I implemented my app in the way you had advised. It works well but looks horrible because in the future I plan to add a lot of task. And if I use specific function approach, at one moment my original task will become too fat. So I decided to ask community for better solution. –  Feb 17 '13 at 11:03
  • But it`s strange enough. In first task I do QTcpSocket* socket = new QTcpSocket; In such way I wanted to throw this socket between tasks. And I got it. I read first byte in the original task, create another task and pass to it socket pointer, after this I do nothing with socket - task finishes. In second task I send through this socket a test message to client app. Client receives this messsage. But then I do 'delete socket;' in the second task destructor, my app crashes. –  Feb 17 '13 at 11:57
  • Even though you don't do anything else to the socket, it is still handling events internally. – JKSH Feb 17 '13 at 12:22
  • Here's the scenario: (1) In Task 1, you create the QTcpSocket in Thread 1, so it "lives" in Thread 1. (2) You read the first byte from the QTcpSocket in Thread 1. (3) You create Task 2 which runs in Thread 2. (4) You pass a pointer of the socket to Thread 2. (5) Task 1 finishes. Thread 1 gets returned to the thread pool. (6) The socket continues to receive data, which triggers internal events. Now, where will the event handlers be run? The answer is: Thread 1! Qt remembers the thread where an object is created -- That's the thread that will handle the object's events/signals. – JKSH Feb 17 '13 at 12:30
  • What's more: Now that Thread 1 has been returned to the thread pool, Qt can use it to run other tasks. If that happens, that thread will be running your new task AND the socket's event handlers. – JKSH Feb 17 '13 at 12:39
  • Remember: QRunnable is designed to run tasks in different threads. You can create your own "Task classes" that don't inherit QRunnable. Choose a class to construct, based on the first byte, and let them run in the same thread as your original task. – JKSH Feb 17 '13 at 12:43
  • JKSH, thanks for your explanations. They are so clear. But I have one more question. Is there any possibility to move object ownership to another thread? I read about QObject::moveToThread() method but as manual said: "this function can only "push" an object from the current thread to another thread". But I need vise versa. It just for interest. –  Feb 17 '13 at 15:54
  • Theoretically yes, you can safely change thread "ownership" via `moveToThread()` if (1) you can find a safe way to pass the `QThread` pointer from your 2nd thread back to your 1st thread, and (2) you can guarantee that the 2nd thread doesn't use the socket until after the 1st thread calls `moveToThread()`. I think that's a very messy process that provides no benefits in your case though :) – JKSH Feb 18 '13 at 03:19
  • One last thing: calling `delete` on a QObject is not recommended -- if it still has events queued for processing, `delete` will cause a crash. use `deleteLater()` instead. Good luck! – JKSH Feb 18 '13 at 03:21