PDA

View Full Version : QWebChannel - QWebSocket - threading problem



RafalNiewinski
5th December 2016, 10:55
Hi,
I am using QWebChannel to expose easy communication interface to connected web browser clients.

Also I have overridden QWebChannelAbstractTransport interface which sends and receives data from/to QWebSocket.

When my client connects and all of the code runs in event loop of main thread all works great,
but when specific action occurs in the application I need to create new thread and move all of the client resources to this thread.
I have properly implemented parent->child hierarchy for moveToThread() operation.

When moveToThread() operation completes successfully and all object runs in new event loop (<- I think) clients can easily send me a message and I can easily process it in new thread,
but when my application trying to send message to client I am receiving console message error: "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread".

I tried to debug it by printing threads in which objects or called methods works and it seems that everything is fine, but doesn't work.


TESTCASE
Part of my QWebChannelAbstractTransport implementation:

Output from qDebug line 11 looks like this: "Client in msg from thread >>> QThread(0x6aa270)"
Output from qDebug line 3 looks like this: "Client out msg from thread <<< QThread(0x6aa270) (socket: QThread(0x6aa270) )"
So it looks like all of object runs in the same thread but in line 6 I have a message: "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread"
Of course socket is QWebSocket object.



void ClientTransportLayer::sendMessage(const QJsonObject &message)
{
qDebug() <<"Client out msg from thread <<< " <<QThread::currentThread() <<" (socket: " <<socket->thread() <<")";

QJsonDocument doc(message);
socket->sendTextMessage(QString::fromUtf8(doc.toJson(QJson Document::Compact)));
}

void ClientTransportLayer::textMessageReceived(const QString &messageData)
{
qDebug() <<"Client in msg from thread >>> " <<QThread::currentThread();

QJsonParseError error;
QJsonDocument message = QJsonDocument::fromJson(messageData.toUtf8(), &error);

if(!error.error && message.isObject())
emit messageReceived(message.object(), this);
}

anda_skoa
6th December 2016, 08:55
Can you show how the application uses this to send messages?
Does this happen from the main thread? Via signal/slot?

One thing you could try debugging-wise is to find the socket notifier message in Qt, set a break point there and then see which thread causes it.

Cheers,
_

RafalNiewinski
6th December 2016, 13:10
Thank you for your interest.

sendMessage is overridden [pure virtual slot] of QWebChannelAbstractTransport

I am using this with QWebChannel so sendMessage is called by QWebChannel mechanism
for example as an automatic confirmation response for method called remotely from client side.
When I need to manually (from my code) send message to client, I simply emit a signal from my object registered for publish in QWebChannel object.


My textMessageReceived is connected to QWebSocket that way in constructor

connect(this->socket, SIGNAL(textMessageReceived(QString)), this, SLOT(textMessageReceived(QString)));


That is simplified backtrace for thread running my objects in case of my first example above


Thread 4 (Thread 0x7fffe73ba700 (LWP 23225)):
#0 ClientTransportLayer::sendMessage (this=0x7fffd8002f90, message=...) at ./src/clienttransportlayer.cpp:22
__PRETTY_FUNCTION__ = "virtual void ClientTransportLayer::sendMessage(const QJsonObject&)"
doc = {static BinaryFormatTag = 1936351857, d = 0x40ece8 <ClientTransportLayer::sendMessage(QJsonObject const&)>}
#1 0x00007ffff6fc7bb6 in QMetaObjectPublisher::handleMessage (this=0x7fffd8002fd0, message=..., transport=0x7fffd8002f90) at /home/rafal/Dokumenty/qtwebchannel/src/webchannel/qmetaobjectpublisher.cpp:641
__PRETTY_FUNCTION__ = "void QMetaObjectPublisher::handleMessage(const QJsonObject&, QWebChannelAbstractTransport*)"
type = TypeInit
#2 0x00007ffff6fc253a in QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1>, QtPrivate::List<QJsonObject const&, QWebChannelAbstractTransport*>, void, void (QMetaObjectPublisher::*)(QJsonObject const&, QWebChannelAbstractTransport*)>::call (f=(void (QMetaObjectPublisher::*)(QMetaObjectPublisher * const, const QJsonObject &, QWebChannelAbstractTransport *)) 0x7ffff6fc7852 <QMetaObjectPublisher::handleMessage(QJsonObject const&, QWebChannelAbstractTransport*)>, o=0x7fffd8002fd0, arg=0x7fffe73b9700) at /home/rafal/Qt/5.7/gcc_64/include/QtCore/qobjectdefs_impl.h:507
#3 0x00007ffff6fc2388 in QtPrivate::FunctionPointer<void (QMetaObjectPublisher::*)(QJsonObject const&, QWebChannelAbstractTransport*)>::call<QtPrivate::List<QJsonObject const&, QWebChannelAbstractTransport*>, void> (f=(void (QMetaObjectPublisher::*)(QMetaObjectPublisher * const, const QJsonObject &, QWebChannelAbstractTransport *)) 0x7ffff6fc7852 <QMetaObjectPublisher::handleMessage(QJsonObject const&, QWebChannelAbstractTransport*)>, o=0x7fffd8002fd0, arg=0x7fffe73b9700) at /home/rafal/Qt/5.7/gcc_64/include/QtCore/qobjectdefs_impl.h:526
#4 0x00007ffff6fc1ea0 in QtPrivate::QSlotObject<void (QMetaObjectPublisher::*)(QJsonObject const&, QWebChannelAbstractTransport*), QtPrivate::List<QJsonObject const&, QWebChannelAbstractTransport*>, void>::impl (which=1, this_=0x7fffd8004640, r=0x7fffd8002fd0, a=0x7fffe73b9700, ret=0x0) at /home/rafal/Qt/5.7/gcc_64/include/QtCore/qobject_impl.h:149
#5 0x00007ffff67d677d in QMetaObject::activate(QObject*, int, int, void**) () from /home/rafal/Qt/5.7/gcc_64/lib/libQt5Core.so.5
#6 0x00007ffff6fd7478 in QWebChannelAbstractTransport::messageReceived (this=0x7fffd8002f90, _t1=..., _t2=0x7fffd8002f90) at .moc/moc_qwebchannelabstracttransport.cpp:150
_a = {0x0, 0x7fffe73b9770, 0x7fffe73b96e8}
#7 0x000000000040f0bb in ClientTransportLayer::textMessageReceived (this=0x7fffd8002f90, messageData=...) at ./src/clienttransportlayer.cpp:37
__PRETTY_FUNCTION__ = "void ClientTransportLayer::textMessageReceived(const QString&)"
error = {offset = 0, error = QJsonParseError::NoError}
message = {static BinaryFormatTag = 1936351857, d = 0x7fffd8004220}
#8 0x000000000041a2bb in ClientTransportLayer::qt_static_metacall (_o=0x7fffd8002f90, _c=QMetaObject::InvokeMetaMethod, _id=0, _a=0x7fffe73b9910) at moc_clienttransportlayer.cpp:71
#9 0x00007ffff67d6056 in QMetaObject::activate(QObject*, int, int, void**) () from /home/rafal/Qt/5.7/gcc_64/lib/libQt5Core.so.5
#10 0x00007ffff7204f65 in QWebSocket::textMessageReceived(QString const&) () from /home/rafal/Qt/5.7/gcc_64/lib/libQt5WebSockets.so.5
#11 0x00007ffff67d677d in QMetaObject::activate(QObject*, int, int, void**) () from /home/rafal/Qt/5.7/gcc_64/lib/libQt5Core.so.5

anda_skoa
6th December 2016, 15:02
I am using this with QWebChannel so sendMessage is called by QWebChannel mechanism

And the QWebChannel has also been moved to the



When I need to manually (from my code) send message to client, I simply emit a signal from my object registered for publish in QWebChannel object.


My textMessageReceived is connected to QWebSocket that way in constructor

connect(this->socket, SIGNAL(textMessageReceived(QString)), this, SLOT(textMessageReceived(QString)));

That looks ok, the cross thread signal/slot connection handling should have transported the call into the thread of "this".

Cheers,
_

RafalNiewinski
7th December 2016, 10:46
Yes of course, all objects was moved to the same (new) thread.
I checked that by calling this on each object and all resides in same thread.

qDebug() <<x.thread();

anda_skoa
8th December 2016, 16:16
Right, then the best choice is likely to see where the call is coming from that triggers the warning.

Cheers,
_