Is it safe to use QClipboard in a background thread
-
Hi,
I noticed parsing clipboard content freezes my application for a moment, so I would like to do it in a background thread. Is it safe to do the following?
QtConcurrent::run([=] { auto text = QApplication::clipboard()->text().toUtf8(); ... });
I have verified it does work but could there be any thread-safety issues here?
-
@Karmo
Just to be clear: I assume you're saying retrieving the text viaQApplication::clipboard()->text()
is what's slow, not the UTF8 conversion? Just check by removing.toUtf8()
. If by any chance it's the latter, you could passQApplication::clipboard()->text()
to the thread.... -
@JonB, it is not toUtf8() that is slow.
auto start = system_clock::now(); auto t = QApplication::clipboard()->text(); qDebug() << " QApplication::clipboard()->text() took" << duration_cast<milliseconds>(system_clock::now() - start).count() << "ms"; auto text = t.toUtf8(); start = system_clock::now(); qDebug() << " toUtf8() took" << duration_cast<milliseconds>(system_clock::now() - start).count() << "ms";
While 9 times out of 10 QApplication::clipboard()->text() takes under 20 ms, sometimes it really hangs the UI.
QApplication::clipboard()->text() took 3622 ms toUtf8() took 0 ms
This was measured on Linux Mint 20 with Qt 5.13 (but did not see any difference in 5.15). On Windows same code was always fast. Maybe something specific to my system.
-
@Karmo
Yes, unfortunately I did suggest/suspect it would not be UTF-ing, under whatever circumstances youur system can take a long time to access the clipboard. 3.6 seconds is not good! Especially if the clipboard does just have text on it, not something like a 1GB pixmap! You may find it's slow if, say, it's the first time you have accessed the clipboard in the current session/boot, or since you put something new on it, with some initializations overhead, at a guess.Given what the guys have said above about it not being thread-safe, you'll have a tricky decision about what you want to do....
-
Not being thread-safe means that you shouldn't access the QClipboard from several threads at once. Using
QtConcurrent::run()
can be problematic in these cases. Instead – if you really want to go down this route – I would suggest using QThread. If you don't overwriteQThread::run()
it will just start an event loop. You can then easily queue lambdas into this thread:QMetaObject::invokeMethod(QAbstractEventDispatcher::instance(thread), [=](){...}, Qt::QueuedConnection);
This approach would make sure that access to the clipboard is synchronized.
However, there is something else to consider: usability! In general, it is the user triggering access to the clipboard. An then the user will expect a certain action on the clipboard to occur. When your program freezes it is an obvious sign that the user still has to wait for the operation to finish. Depending on the specific use case it might be confusing if the program signals that operations on the clipboard are done when this is not the case.
-
@SimonSchroeder said in Is it safe to use QClipboard in a background thread:
This approach would make sure that access to the clipboard is synchronized.
How?
-
@Karmo said in Is it safe to use QClipboard in a background thread:
While 9 times out of 10 QApplication::clipboard()->text() takes under 20 ms, sometimes it really hangs the UI.
QApplication::clipboard()->text() took 3622 ms toUtf8() took 0 ms
Keep in mind that clipboard operations can be interprocess communication. Is the clipboard manager or application that executed the copy operation blocked, paged out, running at particularly low priority, etc at the moment that the pasting application is executing?
This was measured on Linux Mint 20 with Qt 5.13 (but did not see any difference in 5.15). On Windows same code was always fast. Maybe something specific to my system.
Clipboard handling is mostly done by the QPA plugin. Different performance between platforms isn't a surprise.
QClipboard::text(Mode mode) calls QClipboard::mimeData(Mode mode), which then uses platform specific code.As far as safely handling the clipboard in another thread, it's a QObject. The usual rules apply. It might work for a while to QObject::moveToThread() it to a worker thread. That means avoiding lots of code in Qt that expects the clipboard to be in the GUI thread, including the QGuiApplication destructor.
Windowing system permitting, you could spawn a worker process that does nothing but read the clipboard and then send it to the parent over a socket or other asynchronous channel. The delay between the user's request and providing data will still be present but doesn't need to block the UI. As @SimonSchroeder points out, this might be confusing for the user.
-
I reduced the amount of clipboard reading by listening to the dataChanged() signal instead of polling the data. Added benefit is usually nothing is updating on the screen right after clicking Copy, so the intermittent hang is barely noticeable. This is sufficient at the moment but I'll keep the suggestions in mind in case any problems arise.
-
@kshegunov said in Is it safe to use QClipboard in a background thread:
This approach would make sure that access to the clipboard is synchronized.
How?
To be more specific: This synchronizes access to the clipboard for this single program, but not between programs. If all access to the clipboard is sent to the event queue of a single thread it is synchronized. The event queue will only handle one event after the other. Then it does not matter if QClipboard is thread-safe if only a single thread accesses it.
-
@SimonSchroeder said in Is it safe to use QClipboard in a background thread:
@kshegunov said in Is it safe to use QClipboard in a background thread:
This approach would make sure that access to the clipboard is synchronized.
How?
To be more specific: This synchronizes access to the clipboard for this single program, but not between programs. If all access to the clipboard is sent to the event queue of a single thread it is synchronized. The event queue will only handle one event after the other. Then it does not matter if QClipboard is thread-safe if only a single thread accesses it.
Sending an application's clipboard access to a non-gui thread event loop doesn't alter the access for any Qt or 3rd party components, or the QPA plugin.