[Development] QIntrusiveSharedPointer (was: Re: atomic reference counting implementation)

Mutz, Marc marc at kdab.com
Wed Aug 7 22:09:54 CEST 2019


On 2019-08-07 22:01, Konstantin Tokarev wrote:
> It would be even better if it was possible to choose type of reference 
> counting,
> e.g. via template parameter. In many cases atomic counters are not 
> needed
> and just reduce performance.

On 2019-08-07 21:46, Philippe wrote:
> On Wed, 07 Aug 2019 21:00:30 +0300
> "Mutz, Marc" <marc at kdab.com> wrote:
[...]
>>> I think I'm on record saying such impl details as ref-counting for Qt 
>>> implicitly shared classes
>>> should not be public API. This is a perfect example of why I believe 
>>> that to be fundamentally true.
> 
> +1

I have just uploaded my proposed solution for this: 
QIntrusiveSharedPointer. Even though I didn't have a) taking Qt pimpl 
private and b) allowing non-atomic ref counts use-cases in mind when I 
designed it, it supports these use-cases out of the box. I think its 
design will satisfy all the constraints, even though I'd like to see a 
round of internal use first (5.14, or, more likely by now 5.15, make 
public in Qt 6):

- provide a public replacement for 
QSharedDataPointer/QExplicitlySharedDataPointer with all their (known) 
shortcomings fixed

while at the same time:

- allow Qt to use it's own private reference counting (by having 
qshareddatav2_p.h and the Intrusive Protocol implemented like for 
QSharedData); I haven't implemented a QSDV2, yet, but off the top of my 
head the ref-counting is always out-of-line, so it's possible to 
retrofit it into already-released classes

- allow users to use a simple int as ref-count

This is an evolution of 
https://codereview.qt-project.org/c/qt/qtbase/+/115213 and it has all 
the benefits of QtPrivate::SharedDataPointer to, to wit:

- it's possible to have an inline move ctor:

just need to export an out-of-line overload of 
qIntrusiveDeref(QXXXPrivate*), which can call 
qIntrusiveDeref<QXXXPrivate>() if Private is derived from a supported 
QSharedData-alike

- in the same way, even the dtor can be inline
- it's even possible to have all special member functions = default'ed

by exporting qIntrusiveRef() in addition. This reduces the number of DLL 
entry points from 4 (default, copy ctor/assignment op, dtor) to two 
(qIntrusive(De)ref()).

- const propagating
- explicit detach
- proper conversion to bool (unlike QSDP, which detaches, making every 
if (mutable d) tautological)

It also adds

- constexpr default ctor and ctor from nullptr
- implicit ctor from nullptr (but explicit from T*)

Which means that on conforming compilers (not MSVC) a static QISP 
init'ed to nullptr will be placed into the BSS segment and not cause 
dynamic initialisation (even if the dtor is not trivial - the std::mutex 
principle).

The only drawback over QtPrivate::SharedDataPointer is that you need to 
manually declare and implement qIntrusiveRef() and qIntrusiveDeref(); we 
can't hide it in (one) macro. But I think that's a benefit. It reduces 
the number of DLL entry points over the static-Public-member-functions 
design of QtP::SDP, which is probably the reason it consistently 
produces less executable code than QSDP, QESDP and not more code than 
QtP::SDP did (which sometimes, e.g. for QCommandLineArgument, increased 
text size a bit).

Flame away...

Thanks,
Marc


More information about the Development mailing list