[Development] How qAsConst and qExchange lead to qNN

Marc Mutz marc.mutz at qt.io
Thu Nov 17 11:04:54 CET 2022

Hi Thiago,

On 16.11.22 18:50, Thiago Macieira wrote:
> On Tuesday, 15 November 2022 23:50:38 PST Marc Mutz via Development wrote:
>>> in a thread-safe manner (such that if something in
>>> the same thread or another thread-safely modifies that map, the original
>>> user isn't affected).
>> The above isn't thread-safe, it isn't even re-entrant, in the same way
>> that iteration using iterators isn't. This is a known issue whenever you
>> hand out references, and it's nothing that violates our
>> const-is-thread-safe promise,
> No, but it moves the responsibility for avoiding this problem to the user.
> Right now, you can do:
>    for (auto elem : object.keyList()) {
>        operate(); // may recurse back into object and modify it
>    }
> If you use a generator paradigm to return this key list, then the *user* must
> know that they must create a local container with the items to be generated
> and iterate over that. Performance-wise, this no different than if the Qt code
> created the container and returned it, but it has two drawbacks:
> 1) the responsibility for knowing this

Not necessarily. E.g. when the co-routine implementation uses the equivalent of an indexed 
loop, it immunizes itself from changes to the container while it's 
suspended. It can also post a re-entrancy guard in the class' data, like 
we sometimes already do in event handlers and often do in slots, to at least detect and mitigate the issue.

This isn't different from emitting signals or calling virtual functions 
while iterating, and the solutions are the same, and, largely, if not 
completely, under the control of the co-routine implementation.

That said, it's not entirely clear to me how widespread such issues are. After all, the user or a generator sees the potentially-re-entering code, it's in the function he's presently writing/analyzing, and not hidden in the way signal/slot connections or even virtual functions hide the issue by having far-removed code cause the problem. So I don't know whether the benefits of lazy evaluation outweight or are dwarfed by this issue.

> 2) if the Qt object already has a QList with this, then using a generator
> paradigm enforces the need of a deep copy, when implicit would have been
> cheaper

I hasten to interject here that the code you wrote above actually does 
deep-copy in that case (hidden detach in the for loop).

Apart from that, we're circling back to the assumption that a class would hold or return a QList for the sake of QList. For holding, and also for returning, if one must return an owning container, a QVLA or otherwise SBO'd container would be more appropriate in many cases. The lack of such containers in Qt begets the use of QList in the first place. To get out of this tread-mill, one needs to look outside the Qt echo chamber, to std C++ (std::u16string, std::pmr), Folly (F14 (hash table), fbstring (SSO, CoW only for large strings)), Python (strings are QAnyString with SSO there), LLVM (llvm::SmallVector, StringRef, ArrayRef), Mozilla's JS strings (L1/UTF-16 QAnyString, SSO). Then work backwards from these kinds of containers to how we can enable them in Qt.

>>> Because you pointed to QStringTokenizer and that implicitly-
>>> copies a QString.
>> That's imprecise. QStringTokenizer extends rvalue lifetimes ("rvalue
>> pinning") so's to make this safe:
>>      for (auto part : qTokenize(label->text(), u';'))
> BTW, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2012r2.pdf is
> accepted for C++23 and moves the end of the temporaries' lifetimes to the end
> of the full for statement.

Hallelujah! Thanks, Nico!

> Though we still need to work with C++17 and 20 for a while.
> Also, sometimes I wonder if all the work you and I do to optimise these things
> matter, in the end. We may save 0.5% of the CPU time, only for that to be
> dwarfed by whatever QtGui, QtQml are doing.

I hear you, but I'm not ready to give in just yet.


Marc Mutz <marc.mutz at qt.io>
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany

Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B

More information about the Development mailing list