8

I've got a threaded server.

QTcpSocket needs to be created on the thread it needs to be ran on, FI: Qt - Handle QTcpSocket in a new thread by passing the socket descriptor.

My problem is that, I need to have a pool of thread and move the socket on a specific thread AFTER the client has sent a specific token which defines on which thread the socket needs to be.

In other words, I need to read the socket to know on which thread to place it beforehand.

Some idea would be to bind first to a QTcpSocket, read, then send the descriptor to the thread and create another QTcpSocket but the doc says:

Note: It is not possible to initialize two abstract sockets with the same native socket descriptor.

Another solution is to create the socket in a separated thread and then join both thread together, though I don't know if that is possible.

Or perhaps be able to read the socket descriptor on the main thread before calling setSocketDescriptor on the child thread, if that is even possible?

Community
  • 1
  • 1
Damien
  • 1,492
  • 10
  • 32
  • 1
    out of curiosity, why would you have to make the client tell you on which thread you have to run your `QTcpSocket`? – Mike Jul 06 '16 at 09:32
  • *specific token which defines on which thread the socket needs to be* - I wonder how you can specify *which* thread the socket needs to be. AFAIK you can't specify thread id when creating a thread. If you wan to specify thread name, you can just call thread-->setObjectName("Name") and this has nothing to do with sockets. – rightaway717 Jul 06 '16 at 10:44
  • why don't you simply do socket->moveToThread(otherthread)? – λuser Jul 06 '16 at 11:20
  • The dock says it is not allowed – Damien Jul 07 '16 at 10:49
  • You can look at it as a server of cluster of users. A user can create a cluster or join an existing one. Each cluster has its own thread to improve perfomances and use multi-core. – Damien Jul 07 '16 at 10:51

1 Answers1

2

You can absolutely easily move sockets across QThreads, just pay attention to four things:

1) Make sure your QTcpSocket does not have parent before you move

2) Disconnect everything from socket object before move

3) Connect anything you need back in a function which is running in a destination thread (you may use some sort of pool in a thread there those 'moved' objects stored before thread pick them up

4) Call readAll() after init as you may miss some readyRead() signals

Don't see any reasons not to do this if that fits design, at least I used it many times for multithreaded services to split sockets handlers across cores.

evilruff
  • 3,947
  • 1
  • 15
  • 27
  • 1
    You will get warnings from `QIODevice` during writing of data. Because not all fields of `QTcpSocket` can be moved between threads. – Dmitry Sazonov Jul 06 '16 at 23:01
  • Last several years all works fine as I used this technique in many projects.. Can you specify what sort of warning do you mean? – evilruff Jul 06 '16 at 23:04
  • 1
    I'll try to find more, because I have no access to codebase. But there was next warning: "QSocketNotifier: socket notifiers cannot be enabled from another thread" – Dmitry Sazonov Jul 06 '16 at 23:10
  • To be honest with you I never saw anything like this.. I'll have a look in QT sources in the morning as its getting a bit late ) – evilruff Jul 06 '16 at 23:16
  • Just to clarify.. if you move socket to thread, all socket access must happen in that new thead.. you cant move socket to.somewhere and writing from the mainbthread.. it can produce a warning you.mentioned – evilruff Jul 06 '16 at 23:19
  • 2
    There's no need to do any disconnections - that's just silly. The other points are valid. The `QTcpSocket` can definitely be moved between threads, at least on OS X and Windows, I do it all the time and it just works. The socket could have a parent, you then have to unparent it (e.g. by parenting it to a transient object) before you switch threads. – Kuba hasn't forgotten Monica Jul 07 '16 at 14:30
  • Kuba, disconnection needed in case socket was used on other thread, say you have a main thread which is initiating connections, handling error, connected etc signals, so then connection is established and you want to move socket to the worker sometimes it make sense.. its all depends how socket was used before moving to thread.. – evilruff Jul 07 '16 at 14:33
  • It's not something you need to do then you pass socket from nextPendingConnection for example, but there are use cases then it's cleaner to do it – evilruff Jul 07 '16 at 14:34
  • 1
    All that matters is that you access the socket from its `thread()`. Ideally, the controller object that operates the socket would become a parent of the socket, and you'd move the controller to another thread, and along with it the socket follows, and everything is peachy since the connections are direct, but will be invoked from the correct thread's event loop. It's really not supposed to be hard at all when you do it right :) – Kuba hasn't forgotten Monica Jul 07 '16 at 14:35
  • Kuba, I agree with you 101%, and doing it in many many projects, and that's exactly what I am trying to say in my answer =) – evilruff Jul 07 '16 at 14:46
  • @KubaOber , I noticed that in the [Threaded Fortune Server Example](http://doc.qt.io/qt-5/qtnetwork-threadedfortuneserver-example.html) they override `incomingConnection` and pass the native socket descriptor to a new thread. If `QTcpSocket` can be safely moved to other thread, couldn't that be implemented by just getting the `QTcpSocket` from `nextPendingConnection` and move it to a new thread? – Mike Jul 07 '16 at 14:52
  • Please note their statement: *" we are creating this object inside the thread, which automatically associates the socket to the thread's event loop. This ensures that Qt will not try to deliver events to our socket from the main thread while we are accessing it from FortuneThread::run()."*. – Mike Jul 07 '16 at 14:53
  • 1
    It doesnt matter really, Fortune Server example first of all is very very old and in Qt for ages, it's absolutely fine to pass QTcpSocket – evilruff Jul 07 '16 at 15:19
  • So, can anyone confirm that Threaded Fortune Server Example can now be implemented the way I described safely? – Mike Jul 07 '16 at 15:32
  • 1
    Why wouldn't you just take a code, change it and give it a try? =) – evilruff Jul 07 '16 at 15:33
  • Many Qt examples are seriously outdated, unfortunately, and are more a demonstration that Qt strives to keep old code working than how you should implement new functionality. With Qt 5.7 requiring C++11, every example should be rewritten using modern C++ style, etc. – Kuba hasn't forgotten Monica Jul 07 '16 at 15:39
  • @evilruff , I can take a code, but if it doesn't crash or produce warnings, this does NOT imply that it is safe all the time. . . – Mike Jul 07 '16 at 16:32
  • Well, then you have to check it =) and if you get stuck we will be happy to help.. but it's all starts with hands on =) – evilruff Jul 07 '16 at 16:43
  • As well the doc clearly specify this is not allowed, even if that works on sample code, doesn't mean it will on the future version of Qt or may have some undefined behavior. Perhaps that would be a ticket or feature request for Qt ? – Damien Jul 19 '16 at 03:21
  • 1
    on QTcpServer doc, two statements "Note: If you want to handle an incoming connection as a new QTcpSocket object in another thread you have to pass the socketDescriptor to the other thread and create the QTcpSocket object there and use its setSocketDescriptor() method." and "The returned QTcpSocket object cannot be used from another thread. If you want to use an incoming connection from another thread, you need to override incomingConnection()." @evilruff – Damien Mar 10 '17 at 06:17
  • Well, I know that it's written there, but again I didn't change my opinion, I know that it works and looking in Qt sources I don't see any single reason why it shouldn't.. – evilruff Mar 10 '17 at 08:09