rubenvb
rubenvb

Reputation: 76720

Is QwebSocket::sendTextMessage/textMessageReceived async or should I not bother with QMutex/QEventLoop?

I'm trying to send and receive messages over a QWebSocket, but am uncertain as to how safe this all is in a "user pressing all the buttons at the same time" scenario.

Imagine this: there are several buttons that trigger a message sent over the QWebSocket, they receive and process the response, and then display the result. Is there any race condition between the sendTextMessage and textMessageReceived in the sense that sending two messages almost at the same time could trigger a mixup of responses?

What I'm asking is if I need this:

// somewhere in initialization code, I have it in a slot connected to the QWebSocket's "connected" signal
connect(&socket, &QWebSocket::textMessageReceived, this, &client::record_response); // store the response in a member QString called "response"
  connect(&socket, &QWebSocket::textMessageReceived, &wait_for_it, &QEventLoop::quit);

// somewhere after a button is pressed
mutex.lock()
socket.sendTextMessage(message);
eventLoop.exec()

QString the_response = response;
mutex.unlock()
// continue working with the_response

or if a much simpler setup would do (no mutex nor event loop):

// initialization after QWebSocket is connected
connect(&socket, &QWebSocket::textMessageReceived, this, &client::record_response); // store the response in a member QString 

// somewhere after a button is pressed
socket.sendTextMessage(message);
// continue processing "response" immediately

Will the QString response be "filled" immediately after the call to sendTextMessage? I'd think not, but that implies all the QWebSocket machinery is async, and I can't find that explicitly in its documentation. So what should I do to prevent race conditions? (and can I make this even simpler so I don't need any "in-between" variables like both response and the_response, or the event loop?)

EDIT I saw this question (and more importantly this answer) linked in the sidebar, and that has got me thinking that I can just use a lambda instead of this whole setup, but perhaps keeping the mutex as a simple but relatively expensive way to prevent cross-talk between responses and requests. Does this make any sense?

Upvotes: 2

Views: 2484

Answers (1)

Frank Osterfeld
Frank Osterfeld

Reputation: 25165

Qt's I/O/networking-related classes including QWebSocket are asynchronous to avoid blocking the main thread, but never expose any multithreading they might use internally to the user of the API. So unless you work with threads in your own code, there are only very very few cases where one would have use for a QMutex or other explicit thread synchronization (custom painting of QtQuick items is the only one coming to my mind).

textMessageReceived will be emitted in between any other events (user input among them), in a sequence managed by the event loop, but never in a parallel, multithreaded way.

As you already noted, QWebSocket doesn't provide a mechanism to track which response belongs to which request. Mutexes won't help you, and blocking execution is the wrong approach in UI programming. You don't want the user interface to freeze while waiting for the response. Depending on your use case, you could disable the UI e.g. by a modal progress popup or a setEnabled(false) (if using widgets) while waiting for the response, making sure that there's never a second request sent while the UI is waiting for the response of the first. Now that's still better than blocking the UI thread completely, which would even prevent the UI from being painted.

If you want the UI to stay enabled and possibly send more requests while waiting for responses to previous requests, you need to build custom "request manager" logic around QWebSocket matching the responses back to the requests, tracking a queue of unanswered requests and notifying the right request sender as a response arrives. My suggestion would be to follow the Command Pattern also used in QNetworkAccessManager (QNAM): When sending a request, return a Reply object (In QNAM, that's the role of QNetworkReply) to the sender. The sender connects to a "finished" signal of the reply, which is emitted once the corresponding response arrived. That way the sender receives back exactly the wanted response, in an event-driven, asynchronous way, without any explicit waiting and blocking.

Upvotes: 5

Related Questions