Merge two internal classes of QSharedPointer and de-virtualise them
The two classes are QtSharedPointer::ExternalRefCountData and ExternalRefCountWithDestroyFn. The split existed because of what Qt 4.5 did before custom deleters existed: the ExternalRefCountData class was a virtual class that contained a destroy() virtual, which was in charge of deleting the data or returning false if it didn't. Turns out that virtual classes was a mistake. This commit de-virtualises them -- we couldn't do it in Qt 4 because of binary compatibility. This saves us one pointer-size in the size of the private, plus the fact that fewer symbols are required (there is no virtual table to be initialised). Additionaly, since a deleter is always stored with the reference count, we don't need the split between the two classes anymore. Change-Id: I1cd9400561dcee089a406a57bd856b1730f18afc Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com> Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
This commit is contained in:
parent
55c6f09b3a
commit
f77d2e0319
@ -163,18 +163,18 @@
|
|||||||
complete object. See the \tt virtualBaseDifferentPointers autotest for
|
complete object. See the \tt virtualBaseDifferentPointers autotest for
|
||||||
this problem.
|
this problem.
|
||||||
|
|
||||||
The d pointer is of type QtSharedPointer::ExternalRefCountData for simple
|
The d pointer is a pointer to QtSharedPointer::ExternalRefCountData, but it
|
||||||
QSharedPointer objects, but could be of a derived type in some cases. It
|
always points to one of the two classes derived from ExternalRefCountData.
|
||||||
is basically a reference-counted reference-counter.
|
|
||||||
|
|
||||||
\section2 d-pointer
|
\section2 d-pointer
|
||||||
\section3 QtSharedPointer::ExternalRefCountData
|
\section3 QtSharedPointer::ExternalRefCountData
|
||||||
|
|
||||||
This class is basically a reference-counted reference-counter. It has two
|
It is basically a reference-counted reference-counter plus a pointer to the
|
||||||
members: \tt strongref and \tt weakref. The strong reference counter is
|
function to be used to delete the pointer. It has three members: \tt
|
||||||
controlling the lifetime of the object tracked by QSharedPointer. a
|
strongref, \tt weakref, and \tt destroyer. The strong reference counter is
|
||||||
positive value indicates that the object is alive. It's also the number
|
controlling the lifetime of the object tracked by QSharedPointer. A
|
||||||
of QSharedObject instances that are attached to this Data.
|
positive value indicates that the object is alive. It's also the number of
|
||||||
|
QSharedObject instances that are attached to this Data.
|
||||||
|
|
||||||
When the strong reference count decreases to zero, the object is deleted
|
When the strong reference count decreases to zero, the object is deleted
|
||||||
(see below for information on custom deleters). The strong reference
|
(see below for information on custom deleters). The strong reference
|
||||||
@ -189,40 +189,16 @@
|
|||||||
the object tracked derives from QObject, this number is increased by 1,
|
the object tracked derives from QObject, this number is increased by 1,
|
||||||
since QObjectPrivate tracks it too).
|
since QObjectPrivate tracks it too).
|
||||||
|
|
||||||
ExternalRefCountData is a virtual class: it has a virtual destructor and
|
The third member is a pointer to the function that is used to delete the
|
||||||
a virtual destroy() function. The destroy() function is supposed to
|
pointer being tracked. That happens when the destroy() function is called.
|
||||||
delete the object being tracked and return true if it does so. Otherwise,
|
|
||||||
it returns false to indicate that the caller must simply call delete.
|
|
||||||
This allows the normal use-case of QSharedPointer without custom deleters
|
|
||||||
to use only one 12- or 16-byte (depending on whether it's a 32- or 64-bit
|
|
||||||
architecture) external descriptor structure, without paying the price for
|
|
||||||
the custom deleter that it isn't using.
|
|
||||||
|
|
||||||
\section3 QtSharedPointer::ExternalRefCountDataWithDestroyFn
|
The size of this class is the size of the two atomic ints plus the size of
|
||||||
|
a pointer. On 32-bit architectures, that's 12 bytes, whereas on 64-bit ones
|
||||||
This class is not used directly, per se. It only exists to enable the two
|
it's 16 bytes. There is no padding.
|
||||||
classes that derive from it. It adds one member variable, which is a
|
|
||||||
pointer to a function (which returns void and takes an
|
|
||||||
ExternalRefCountData* as a parameter). It also overrides the destroy()
|
|
||||||
function: it calls that function pointer with \tt this as parameter, and
|
|
||||||
returns true.
|
|
||||||
|
|
||||||
That means when ExternalRefCountDataWithDestroyFn is used, the \tt
|
|
||||||
destroyer field must be set to a valid function that \b will delete the
|
|
||||||
object tracked.
|
|
||||||
|
|
||||||
This class also adds an operator delete function to ensure that it simply
|
|
||||||
calls the global operator delete. That should be the behaviour in all
|
|
||||||
compilers already, but to be on the safe side, this class ensures that no
|
|
||||||
funny business happens.
|
|
||||||
|
|
||||||
On a 32-bit architecture, this class is 16 bytes in size, whereas it's 24
|
|
||||||
bytes on 64-bit. (On Itanium where function pointers contain the global
|
|
||||||
pointer, it can be 32 bytes).
|
|
||||||
|
|
||||||
\section3 QtSharedPointer::ExternalRefCountWithCustomDeleter
|
\section3 QtSharedPointer::ExternalRefCountWithCustomDeleter
|
||||||
|
|
||||||
This class derives from ExternalRefCountDataWithDestroyFn and is a
|
This class derives from ExternalRefCountData and is a
|
||||||
template class. As template parameters, it has the type of the pointer
|
template class. As template parameters, it has the type of the pointer
|
||||||
being tracked (\tt T) and a \tt Deleter, which is anything. It adds two
|
being tracked (\tt T) and a \tt Deleter, which is anything. It adds two
|
||||||
fields to its parent class, matching those template parameters: a member
|
fields to its parent class, matching those template parameters: a member
|
||||||
@ -236,31 +212,26 @@
|
|||||||
the deleter in the generic case.
|
the deleter in the generic case.
|
||||||
|
|
||||||
This class is never instantiated directly: the constructors and
|
This class is never instantiated directly: the constructors and
|
||||||
destructor are private. Only the create() function may be called to
|
destructor are private and, in C++11, deleted. Only the create() function
|
||||||
return an object of this type. See below for construction details.
|
may be called to return an object of this type. See below for construction
|
||||||
|
details.
|
||||||
|
|
||||||
The size of this class depends on the size of \tt Deleter. If it's an
|
The size of this class depends on the size of \tt Deleter. If it's an empty
|
||||||
empty functor (i.e., no members), ABIs generally assign it the size of 1.
|
functor (i.e., no members), ABIs generally assign it the size of 1. But
|
||||||
But given that it's followed by a pointer, up to 3 or 7 padding bytes may
|
given that it's followed by a pointer, padding bytes may be inserted so
|
||||||
be inserted: in that case, the size of this class is 16+4+4 = 24 bytes on
|
that the alignment of the class and of the pointer are correct. In that
|
||||||
32-bit architectures, or 24+8+8 = 40 bytes on 64-bit architectures (48
|
case, the size of this class is 12+4+4 = 20 bytes on 32-bit architectures,
|
||||||
bytes on Itanium with global pointers stored). If \tt Deleter is a
|
or 16+8+8 = 40 bytes on 64-bit architectures. If \tt Deleter is a function
|
||||||
function pointer, the size should be the same as the empty structure
|
pointer, the size should be the same as the empty structure case. If \tt
|
||||||
case, except for Itanium where it may be 56 bytes due to another global
|
Deleter is a pointer to a member function (PMF), the size will be bigger
|
||||||
pointer. If \tt Deleter is a pointer to a member function (PMF), the size
|
and will depend on the ABI. For architectures using the Itanium C++ ABI, a
|
||||||
will be even bigger and will depend on the ABI. For architectures using
|
PMF is twice the size of a normal pointer. In that case, the size of this
|
||||||
the Itanium C++ ABI, a PMF is twice the size of a normal pointer, or 24
|
structure will be 12+8+4 = 24 bytes on 32-bit architectures, 16+16+8 = 40
|
||||||
bytes on Itanium itself. In that case, the size of this structure will be
|
bytes on 64-bit ones.
|
||||||
16+8+4 = 28 bytes on 32-bit architectures, 24+16+8 = 48 bytes on 64-bit,
|
|
||||||
and 32+24+8 = 64 bytes on Itanium.
|
|
||||||
|
|
||||||
(Values for Itanium consider an LP64 architecture; for ILP32, pointers
|
|
||||||
are 32-bit in length, function pointers are 64-bit and PMF are 96-bit, so
|
|
||||||
the sizes are slightly less)
|
|
||||||
|
|
||||||
\section3 QtSharedPointer::ExternalRefCountWithContiguousData
|
\section3 QtSharedPointer::ExternalRefCountWithContiguousData
|
||||||
|
|
||||||
This class also derives from ExternalRefCountDataWithDestroyFn and it is
|
This class also derives from ExternalRefCountData and it is
|
||||||
also a template class. The template parameter is the type \tt T of the
|
also a template class. The template parameter is the type \tt T of the
|
||||||
class which QSharedPointer tracks. It adds only one member to its parent,
|
class which QSharedPointer tracks. It adds only one member to its parent,
|
||||||
which is of type \tt T (the actual type, not a pointer to it).
|
which is of type \tt T (the actual type, not a pointer to it).
|
||||||
@ -273,8 +244,8 @@
|
|||||||
|
|
||||||
Like ExternalRefCountWithCustomDeleter, this class is never instantiated
|
Like ExternalRefCountWithCustomDeleter, this class is never instantiated
|
||||||
directly. This class also provides a create() member that returns the
|
directly. This class also provides a create() member that returns the
|
||||||
pointer, and hides its constructors and destructor. (With C++0x, we'd
|
pointer, and hides its constructors and destructor. With C++11, they're
|
||||||
delete them).
|
deleted.
|
||||||
|
|
||||||
The size of this class depends on the size of \tt T.
|
The size of this class depends on the size of \tt T.
|
||||||
|
|
||||||
@ -287,12 +258,8 @@
|
|||||||
|
|
||||||
Instead of instantiating the class by the normal way, the create() method
|
Instead of instantiating the class by the normal way, the create() method
|
||||||
calls \tt{operator new} directly with the size of the class, then calls
|
calls \tt{operator new} directly with the size of the class, then calls
|
||||||
the parent class's constructor only (ExternalRefCountDataWithDestroyFn).
|
the parent class's constructor only (that is, ExternalRefCountData's constructor).
|
||||||
This ensures that the inherited members are initialised properly, as well
|
This ensures that the inherited members are initialised properly.
|
||||||
as the virtual table pointer, which must point to
|
|
||||||
ExternalRefCountDataWithDestroyFn's virtual table. That way, we also
|
|
||||||
ensure that the virtual destructor being called is
|
|
||||||
ExternalRefCountDataWithDestroyFn's.
|
|
||||||
|
|
||||||
After initialising the base class, the
|
After initialising the base class, the
|
||||||
ExternalRefCountWithCustomDeleter::create() function initialises the new
|
ExternalRefCountWithCustomDeleter::create() function initialises the new
|
||||||
@ -305,7 +272,7 @@
|
|||||||
|
|
||||||
When initialising the parent class, the create() functions pass the
|
When initialising the parent class, the create() functions pass the
|
||||||
address of the static deleter() member function. That is, when the
|
address of the static deleter() member function. That is, when the
|
||||||
virtual destroy() is called by QSharedPointer, the deleter() functions
|
destroy() function is called by QSharedPointer, the deleter() functions
|
||||||
are called instead. These functions static_cast the ExternalRefCountData*
|
are called instead. These functions static_cast the ExternalRefCountData*
|
||||||
parameter to their own type and execute their deletion: for the
|
parameter to their own type and execute their deletion: for the
|
||||||
ExternalRefCountWithCustomDeleter::deleter() case, it runs the user's
|
ExternalRefCountWithCustomDeleter::deleter() case, it runs the user's
|
||||||
@ -313,12 +280,7 @@
|
|||||||
ExternalRefCountWithContiguousData::deleter, it simply calls the \tt T
|
ExternalRefCountWithContiguousData::deleter, it simply calls the \tt T
|
||||||
destructor directly.
|
destructor directly.
|
||||||
|
|
||||||
By not calling the constructor of the derived classes, we avoid
|
Only one non-inline function is required per template, which is
|
||||||
instantiating their virtual tables. Since these classes are
|
|
||||||
template-based, there would be one virtual table per \tt T and \tt
|
|
||||||
Deleter type. (This is what Qt 4.5 did.)
|
|
||||||
|
|
||||||
Instead, only one non-inline function is required per template, which is
|
|
||||||
the deleter() static member. All the other functions can be inlined.
|
the deleter() static member. All the other functions can be inlined.
|
||||||
What's more, the address of deleter() is calculated only in code, which
|
What's more, the address of deleter() is calculated only in code, which
|
||||||
can be resolved at link-time if the linker can determine that the
|
can be resolved at link-time if the linker can determine that the
|
||||||
@ -326,11 +288,6 @@
|
|||||||
classes are not exported, that is the case for Windows or for builds with
|
classes are not exported, that is the case for Windows or for builds with
|
||||||
\tt{-fvisibility=hidden}).
|
\tt{-fvisibility=hidden}).
|
||||||
|
|
||||||
In contrast, a virtual table would require at least 3 relocations to be
|
|
||||||
resolved at module load-time, per module where these classes are used.
|
|
||||||
(In the Itanium C++ ABI, there would be more relocations, due to the
|
|
||||||
RTTI)
|
|
||||||
|
|
||||||
\section3 Modifications due to pointer-tracking
|
\section3 Modifications due to pointer-tracking
|
||||||
|
|
||||||
To ensure that pointers created with pointer-tracking enabled get
|
To ensure that pointers created with pointer-tracking enabled get
|
||||||
@ -340,9 +297,9 @@
|
|||||||
|
|
||||||
When ExternalRefCountWithCustomDeleter or
|
When ExternalRefCountWithCustomDeleter or
|
||||||
ExternalRefCountWithContiguousData are used, their create() functions
|
ExternalRefCountWithContiguousData are used, their create() functions
|
||||||
will set the ExternalRefCountDataWithDestroyFn::destroyer function
|
will set the ExternalRefCountData::destroyer function
|
||||||
pointer to safetyCheckDeleter() instead. These static member functions
|
pointer to safetyCheckDeleter() instead. These static member functions
|
||||||
simply call internalSafetyCheckRemove2() before passing control to the
|
simply call internalSafetyCheckRemove() before passing control to the
|
||||||
normal deleter() function.
|
normal deleter() function.
|
||||||
|
|
||||||
If neither custom deleter nor QSharedPointer::create() are used, then
|
If neither custom deleter nor QSharedPointer::create() are used, then
|
||||||
|
@ -171,21 +171,27 @@ namespace QtSharedPointer {
|
|||||||
// reference counter, and it tracks the lifetime of the pointer itself.
|
// reference counter, and it tracks the lifetime of the pointer itself.
|
||||||
// "weakref" is the outer reference counter and it tracks the lifetime of
|
// "weakref" is the outer reference counter and it tracks the lifetime of
|
||||||
// the ExternalRefCountData object.
|
// the ExternalRefCountData object.
|
||||||
|
//
|
||||||
|
// The deleter is stored in the destroyer member and is always a pointer to
|
||||||
|
// a static function in ExternalRefCountWithCustomDeleter or in
|
||||||
|
// ExternalRefCountWithContiguousData
|
||||||
struct ExternalRefCountData
|
struct ExternalRefCountData
|
||||||
{
|
{
|
||||||
|
typedef void (*DestroyerFn)(ExternalRefCountData *);
|
||||||
QBasicAtomicInt weakref;
|
QBasicAtomicInt weakref;
|
||||||
QBasicAtomicInt strongref;
|
QBasicAtomicInt strongref;
|
||||||
|
DestroyerFn destroyer;
|
||||||
|
|
||||||
inline ExternalRefCountData()
|
inline ExternalRefCountData(DestroyerFn d)
|
||||||
|
: destroyer(d)
|
||||||
{
|
{
|
||||||
strongref.store(1);
|
strongref.store(1);
|
||||||
weakref.store(1);
|
weakref.store(1);
|
||||||
}
|
}
|
||||||
inline ExternalRefCountData(Qt::Initialization) { }
|
inline ExternalRefCountData(Qt::Initialization) { }
|
||||||
virtual inline ~ExternalRefCountData() { Q_ASSERT(!weakref.load()); Q_ASSERT(strongref.load() <= 0); }
|
~ExternalRefCountData() { Q_ASSERT(!weakref.load()); Q_ASSERT(strongref.load() <= 0); }
|
||||||
|
|
||||||
// overridden by derived classes
|
void destroy() { destroyer(this); }
|
||||||
virtual inline void destroy() { }
|
|
||||||
|
|
||||||
#ifndef QT_NO_QOBJECT
|
#ifndef QT_NO_QOBJECT
|
||||||
Q_CORE_EXPORT static ExternalRefCountData *getAndRef(const QObject *);
|
Q_CORE_EXPORT static ExternalRefCountData *getAndRef(const QObject *);
|
||||||
@ -194,34 +200,21 @@ namespace QtSharedPointer {
|
|||||||
#endif
|
#endif
|
||||||
inline void checkQObjectShared(...) { }
|
inline void checkQObjectShared(...) { }
|
||||||
inline void setQObjectShared(...) { }
|
inline void setQObjectShared(...) { }
|
||||||
};
|
|
||||||
// sizeof(ExternalRefCount) = 12 (32-bit) / 16 (64-bit)
|
|
||||||
|
|
||||||
// This class extends ExternalRefCountData with a pointer
|
|
||||||
// to a function, which is called by the destroy() function.
|
|
||||||
struct ExternalRefCountWithDestroyFn: public ExternalRefCountData
|
|
||||||
{
|
|
||||||
typedef void (*DestroyerFn)(ExternalRefCountData *);
|
|
||||||
DestroyerFn destroyer;
|
|
||||||
|
|
||||||
inline ExternalRefCountWithDestroyFn(DestroyerFn d)
|
|
||||||
: destroyer(d)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
inline void destroy() { destroyer(this); }
|
|
||||||
inline void operator delete(void *ptr) { ::operator delete(ptr); }
|
inline void operator delete(void *ptr) { ::operator delete(ptr); }
|
||||||
inline void operator delete(void *, void *) { }
|
inline void operator delete(void *, void *) { }
|
||||||
};
|
};
|
||||||
// sizeof(ExternalRefCountWithDestroyFn) = 16 (32-bit) / 24 (64-bit)
|
// sizeof(ExternalRefCountData) = 12 (32-bit) / 16 (64-bit)
|
||||||
|
|
||||||
// This class extends ExternalRefCountWithDestroyFn and implements
|
// This class extends ExternalRefCountData and implements
|
||||||
// the static function that deletes the object. The pointer and the
|
// the static function that deletes the object. The pointer and the
|
||||||
// custom deleter are kept in the "extra" member.
|
// custom deleter are kept in the "extra" member so we can construct
|
||||||
|
// and destruct it independently of the full structure.
|
||||||
template <class T, typename Deleter>
|
template <class T, typename Deleter>
|
||||||
struct ExternalRefCountWithCustomDeleter: public ExternalRefCountWithDestroyFn
|
struct ExternalRefCountWithCustomDeleter: public ExternalRefCountData
|
||||||
{
|
{
|
||||||
typedef ExternalRefCountWithCustomDeleter Self;
|
typedef ExternalRefCountWithCustomDeleter Self;
|
||||||
typedef ExternalRefCountWithDestroyFn BaseClass;
|
typedef ExternalRefCountData BaseClass;
|
||||||
|
|
||||||
struct CustomDeleter
|
struct CustomDeleter
|
||||||
{
|
{
|
||||||
@ -231,7 +224,8 @@ namespace QtSharedPointer {
|
|||||||
inline CustomDeleter(T *p, Deleter d) : deleter(d), ptr(p) {}
|
inline CustomDeleter(T *p, Deleter d) : deleter(d), ptr(p) {}
|
||||||
};
|
};
|
||||||
CustomDeleter extra;
|
CustomDeleter extra;
|
||||||
// sizeof(CustomDeleter) = sizeof(Deleter) + sizeof(void*)
|
// sizeof(CustomDeleter) = sizeof(Deleter) + sizeof(void*) + padding
|
||||||
|
// for Deleter = stateless functor: 8 (32-bit) / 16 (64-bit) due to padding
|
||||||
// for Deleter = function pointer: 8 (32-bit) / 16 (64-bit)
|
// for Deleter = function pointer: 8 (32-bit) / 16 (64-bit)
|
||||||
// for Deleter = PMF: 12 (32-bit) / 24 (64-bit) (GCC)
|
// for Deleter = PMF: 12 (32-bit) / 24 (64-bit) (GCC)
|
||||||
|
|
||||||
@ -265,19 +259,20 @@ namespace QtSharedPointer {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
// prevent construction and the emission of virtual symbols
|
// prevent construction
|
||||||
ExternalRefCountWithCustomDeleter();
|
ExternalRefCountWithCustomDeleter() Q_DECL_EQ_DELETE;
|
||||||
~ExternalRefCountWithCustomDeleter();
|
~ExternalRefCountWithCustomDeleter() Q_DECL_EQ_DELETE;
|
||||||
|
Q_DISABLE_COPY(ExternalRefCountWithCustomDeleter)
|
||||||
};
|
};
|
||||||
|
|
||||||
// This class extends ExternalRefCountWithDestroyFn and adds a "T"
|
// This class extends ExternalRefCountData and adds a "T"
|
||||||
// member. That way, when the create() function is called, we allocate
|
// member. That way, when the create() function is called, we allocate
|
||||||
// memory for both QSharedPointer's d-pointer and the actual object being
|
// memory for both QSharedPointer's d-pointer and the actual object being
|
||||||
// tracked.
|
// tracked.
|
||||||
template <class T>
|
template <class T>
|
||||||
struct ExternalRefCountWithContiguousData: public ExternalRefCountWithDestroyFn
|
struct ExternalRefCountWithContiguousData: public ExternalRefCountData
|
||||||
{
|
{
|
||||||
typedef ExternalRefCountWithDestroyFn Parent;
|
typedef ExternalRefCountData Parent;
|
||||||
T data;
|
T data;
|
||||||
|
|
||||||
static void deleter(ExternalRefCountData *self)
|
static void deleter(ExternalRefCountData *self)
|
||||||
@ -311,9 +306,10 @@ namespace QtSharedPointer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// prevent construction and the emission of virtual symbols
|
// prevent construction
|
||||||
ExternalRefCountWithContiguousData();
|
ExternalRefCountWithContiguousData() Q_DECL_EQ_DELETE;
|
||||||
~ExternalRefCountWithContiguousData();
|
~ExternalRefCountWithContiguousData() Q_DECL_EQ_DELETE;
|
||||||
|
Q_DISABLE_COPY(ExternalRefCountWithContiguousData)
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is the main body of QSharedPointer. It implements the
|
// This is the main body of QSharedPointer. It implements the
|
||||||
|
Loading…
Reference in New Issue
Block a user