summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@nokia.com>2009-08-12 09:52:15 (GMT)
committerThiago Macieira <thiago.macieira@nokia.com>2009-08-13 15:26:59 (GMT)
commita2cc46c89e73d089f333423f8382eb7582699e39 (patch)
tree66fb6003a255f95af5bc2e6cb8229f5d26bd3aa6 /src/corelib
parente2bb75e66f7ef9dda6fae489ebbb30ffb5e9e37e (diff)
downloadQt-a2cc46c89e73d089f333423f8382eb7582699e39.zip
Qt-a2cc46c89e73d089f333423f8382eb7582699e39.tar.gz
Qt-a2cc46c89e73d089f333423f8382eb7582699e39.tar.bz2
Internal doc: explain how QSharedPointer works
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/tools/qsharedpointer.cpp266
-rw-r--r--src/corelib/tools/qsharedpointer_impl.h57
2 files changed, 305 insertions, 18 deletions
diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp
index e3e1db6..60c7db4 100644
--- a/src/corelib/tools/qsharedpointer.cpp
+++ b/src/corelib/tools/qsharedpointer.cpp
@@ -107,6 +107,249 @@
cases, since they have the same functionality. See
\l{QWeakPointer#tracking-qobject} for more information.
+ \section1 Optional pointer tracking
+
+ A feature of QSharedPointer that can be enabled at compile-time for
+ debugging purposes is a pointer tracking mechanism. When enabled,
+ QSharedPointer registers in a global set all the pointers that it tracks.
+ This allows one to catch mistakes like assigning the same pointer to two
+ QSharedPointer objects.
+
+ This function is enabled by defining the \tt{QT_SHAREDPOINTER_TRACK_POINTERS}
+ macro before including the QSharedPointer header.
+
+ It is safe to use this feature even with code compiled without the
+ feature. QSharedPointer will ensure that the pointer is removed from the
+ tracker even from code compiled without pointer tracking.
+
+ Note, however, that the pointer tracking feature has limitations on
+ multiple- or virtual-inheritance (that is, in cases where two different
+ pointer addresses can refer to the same object). In that case, if a
+ pointer is cast to a different type and its value changes,
+ QSharedPointer's pointer tracking mechanism mail fail to detect that the
+ object being tracked is the same.
+
+ \omit
+ \secton1 QSharedPointer internals
+
+ QSharedPointer is in reality implemented by two ancestor classes:
+ QtSharedPointer::Basic and QtSharedPointer::ExternalRefCount. The reason
+ for having that split is now mostly legacy: in the beginning,
+ QSharedPointer was meant to support both internal reference counting and
+ external reference counting.
+
+ QtSharedPointer::Basic implements the basic functionality that is shared
+ between internal- and external-reference counting. That is, it's mostly
+ the accessor functions into QSharedPointer. Those are all inherited by
+ QSharedPointer, which adds another level of shared functionality (the
+ constructors and assignment operators). The Basic class has one member
+ variable, which is the actual pointer being tracked.
+
+ QtSharedPointer::ExternalRefCount implements the actual reference
+ counting and introduces the d-pointer for QSharedPointer. That d-pointer
+ itself is shared with with other QSharedPointer objects as well as
+ QWeakPointer.
+
+ The reason for keeping the pointer value itself outside the d-pointer is
+ because of multiple inheritance needs. If you have two QSharedPointer
+ objects of different pointer types, but pointing to the same object in
+ memory, it could happen that the pointer values are different. The \tt
+ differentPointers autotest exemplifies this problem. The same thing could
+ happen in the case of virtual inheritance: a pointer of class matching
+ the virtual base has different address compared to the pointer of the
+ complete object. See the \tt virtualBaseDifferentPointers autotest for
+ this problem.
+
+ The d pointer is of type QtSharedPointer::ExternalRefCountData for simple
+ QSharedPointer objects, but could be of a derived type in some cases. It
+ is basically a reference-counted reference-counter.
+
+ \section2 d-pointer
+ \section3 QtSharedPointer::ExternalRefCountData
+
+ This class is basically a reference-counted reference-counter. It has two
+ members: \tt strongref and \tt weakref. The strong reference counter is
+ controlling the lifetime of the object tracked by QSharedPointer. a
+ 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
+ (see below for information on custom deleters). The strong reference
+ count can also exceptionally be -1, indicating that there are no
+ QSharedPointers attached to an object, which is tracked too. The only
+ case where this is possible is that of
+ \l{QWeakPointer#tracking-qobject}{QWeakPointers tracking a QObject}.
+
+ The weak reference count controls the lifetime of the d-pointer itself.
+ It can be thought of as an internal/intrusive reference count for
+ ExternalRefCountData itself. This count is equal to the number of
+ QSharedPointers and QWeakPointers that are tracking this object. (In case
+ the object tracked derives from QObject, this number is increased by 1,
+ since QObjectPrivate tracks it too).
+
+ ExternalRefCountData is a virtual class: it has a virtual destructor and
+ a virtual destroy() function. The destroy() function is supposed to
+ 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
+
+ This class is not used directly, per se. It only exists to enable the two
+ 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 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
+
+ This class derives from ExternalRefCountDataWithDestroyFn and is a
+ 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
+ fields to its parent class, matching those template parameters: a member
+ of type \tt Deleter and a member of type \tt T*.
+
+ The purpose of this class is to store the pointer to be deleted and the
+ deleter code along with the d-pointer. This allows the last strong
+ reference to call any arbitrary function that disposes of the object. For
+ example, this allows calling QObject::deleteLater() on a given object.
+ The pointer to the object is kept here to avoid the extra cost of keeping
+ the deleter in the generic case.
+
+ This class is never instantiated directly: the constructors and
+ destructor are private. Only the create() function 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
+ empty functor (i.e., no members), ABIs generally assign it the size of 1.
+ But given that it's followed by a pointer, up to 3 or 7 padding bytes may
+ be inserted: in that case, the size of this class is 16+4+4 = 24 bytes on
+ 32-bit architectures, or 24+8+8 = 40 bytes on 64-bit architectures (48
+ bytes on Itanium with global pointers stored). If \tt Deleter is a
+ function pointer, the size should be the same as the empty structure
+ case, except for Itanium where it may be 56 bytes due to another global
+ pointer. If \tt Deleter is a pointer to a member function (PMF), the size
+ will be even bigger and will depend on the ABI. For architectures using
+ the Itanium C++ ABI, a PMF is twice the size of a normal pointer, or 24
+ bytes on Itanium itself. In that case, the size of this structure will be
+ 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
+
+ This class also derives from ExternalRefCountDataWithDestroyFn and it is
+ 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,
+ which is of type \tt T (the actual type, not a pointer to it).
+
+ The purpose of this class is to lay the \tt T object out next to the
+ reference counts, saving one memory allocation per shared pointer. This
+ is particularly interesting for small \tt T or for the cases when there
+ are few if any QWeakPointer tracking the object. This class exists to
+ implement the QSharedPointer::create() call.
+
+ Like ExternalRefCountWithCustomDeleter, this class is never instantiated
+ directly. This class also provides a create() member that returns the
+ pointer, and hides its constructors and destructor. (With C++0x, we'd
+ delete them).
+
+ The size of this class depends on the size of \tt T.
+
+ \section3 Instantiating ExternalRefCountWithCustomDeleter and ExternalRefCountWithContiguousData
+
+ Like explained above, these classes have private constructors. Moreover,
+ they are not defined anywhere, so trying to call \tt{new ClassType} would
+ result in a compilation or linker error. Instead, these classes must be
+ constructed via their create() methods.
+
+ 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
+ the parent class's constructor only (ExternalRefCountDataWithDestroyFn).
+ This ensures that the inherited members are initialised properly, as well
+ 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
+ ExternalRefCountWithCustomDeleter::create() function initialises the new
+ members directly, by using the placement \tt{operator new}. In the case
+ of the ExternalRefCountWithContiguousData::create() function, the address
+ to the still-uninitialised \tt T member is saved for the callee to use.
+ The member is only initialised in QSharedPointer::create(), so that we
+ avoid having many variants of the internal functions according to the
+ arguments in use for calling the constructor.
+
+ When initialising the parent class, the create() functions pass the
+ address of the static deleter() member function. That is, when the
+ virtual destroy() is called by QSharedPointer, the deleter() functions
+ are called instead. These functiosn static_cast the ExternalRefCountData*
+ parameter to their own type and execute their deletion: for the
+ ExternalRefCountWithCustomDeleter::deleter() case, it runs the user's
+ custom deleter, then destroys the deleter; for
+ ExternalRefCountWithContiguousData::deleter, it simply calls the \tt T
+ destructor directly.
+
+ By not calling the constructor of the derived classes, we avoid
+ 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.
+ 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
+ function lies in the current application or library module (since these
+ classes are not exported, that is the case for Windows or for builds with
+ \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
+
+ To ensure that pointers created with pointer-tracking enabled get
+ un-tracked when destroyed, even if destroyed by code compiled without the
+ feature, QSharedPointer modifies slightly the instructions of the
+ previous sections.
+
+ When ExternalRefCountWithCustomDeleter or
+ ExternalRefCountWithContiguousData are used, their create() functions
+ will set the ExternalRefCountDataWithDestroyFn::destroyer function
+ pointer to safetyCheckDeleter() instead. These static member functions
+ simply call internalSafetyCheckRemove2() before passing control to the
+ normal deleter() function.
+
+ If neither custom deleter nor QSharedPointer::create() are used, then
+ QSharedPointer uses a custom deleter of its own: the normalDeleter()
+ function, which simply calls \tt delete. By using a custom deleter, the
+ safetyCheckDeleter() procedure described above kicks in.
+
+ \endomit
+
\sa QSharedDataPointer, QWeakPointer
*/
@@ -182,6 +425,29 @@
QWeakPointers created from QObject should never be passed to code that
hasn't been recompiled.
+ \omit
+ \secton1 QWeakPointer internals
+
+ QWeakPointer shares most of its internal functionality with
+ \l{QSharedPointer#qsharedpointer-internals}{QSharedPointer}, so see that
+ class's internal documentation for more information.
+
+ QWeakPointer requires an external reference counter in order to operate.
+ Therefore, it is incompatible by design with \l QSharedData-derived
+ classes.
+
+ It has a special QObject constructor, which works by calling
+ QtSharedPointer::ExternalRefCountData::getAndRef, which retrieves the
+ d-pointer from QObjectPrivate. If one isn't set yet, that function
+ creates the d-pointer and atomically sets it.
+
+ If getAndRef needs to create a d-pointer, it sets the strongref to -1,
+ indicating that the QObject is not shared: QWeakPointer is used only to
+ determine whether the QObject has been deleted. In that case, it cannot
+ be upgraded to QSharedPointer (see the previous section).
+
+ \endomit
+
\sa QSharedPointer
*/
diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h
index ba479f9..cbeb79f 100644
--- a/src/corelib/tools/qsharedpointer_impl.h
+++ b/src/corelib/tools/qsharedpointer_impl.h
@@ -115,6 +115,9 @@ namespace QtSharedPointer {
template <class T> struct RemovePointer<QSharedPointer<T> > { typedef T Type; };
template <class T> struct RemovePointer<QWeakPointer<T> > { typedef T Type; };
+ // This class provides the basic functionality of a pointer wrapper.
+ // Its existence is mostly legacy, since originally QSharedPointer
+ // could also be used for internally-refcounted objects.
template <class T>
class Basic
{
@@ -155,6 +158,12 @@ namespace QtSharedPointer {
Type *value;
};
+ // This class is the d-pointer of QSharedPointer and QWeakPointer.
+ //
+ // It is a reference-counted reference counter. "strongref" is the inner
+ // reference counter, and it tracks the lifetime of the pointer itself.
+ // "weakref" is the outer reference counter and it tracks the lifetime of
+ // the ExternalRefCountData object.
struct ExternalRefCountData
{
QBasicAtomicInt weakref;
@@ -168,6 +177,9 @@ namespace QtSharedPointer {
inline ExternalRefCountData(Qt::Initialization) { }
virtual inline ~ExternalRefCountData() { Q_ASSERT(!weakref); Q_ASSERT(strongref <= 0); }
+ // overridden by derived classes
+ // returns false to indicate caller should delete the pointer
+ // returns true in case it has already done so
virtual inline bool destroy() { return false; }
#ifndef QT_NO_QOBJECT
@@ -178,18 +190,8 @@ namespace QtSharedPointer {
};
// sizeof(ExternalRefCount) = 12 (32-bit) / 16 (64-bit)
- template <class T, typename Deleter>
- struct CustomDeleter
- {
- Deleter deleter;
- T *ptr;
-
- inline CustomDeleter(T *p, Deleter d) : deleter(d), ptr(p) {}
- };
- // sizeof(CustomDeleter) = sizeof(Deleter) + sizeof(void*)
- // for Deleter = function pointer: 8 (32-bit) / 16 (64-bit)
- // for Deleter = PMF: 12 (32-bit) / 24 (64-bit) (GCC)
-
+ // 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 *);
@@ -204,13 +206,26 @@ namespace QtSharedPointer {
};
// sizeof(ExternalRefCountWithDestroyFn) = 16 (32-bit) / 24 (64-bit)
+ // This class extends ExternalRefCountWithDestroyFn and implements
+ // the static function that deletes the object. The pointer and the
+ // custom deleter are kept in the "extra" member.
template <class T, typename Deleter>
struct ExternalRefCountWithCustomDeleter: public ExternalRefCountWithDestroyFn
{
typedef ExternalRefCountWithCustomDeleter Self;
- typedef ExternalRefCountWithDestroyFn Parent;
- typedef CustomDeleter<T, Deleter> Next;
- Next extra;
+ typedef ExternalRefCountWithDestroyFn BaseClass;
+
+ struct CustomDeleter
+ {
+ Deleter deleter;
+ T *ptr;
+
+ inline CustomDeleter(T *p, Deleter d) : deleter(d), ptr(p) {}
+ };
+ CustomDeleter extra;
+ // sizeof(CustomDeleter) = sizeof(Deleter) + sizeof(void*)
+ // for Deleter = function pointer: 8 (32-bit) / 16 (64-bit)
+ // for Deleter = PMF: 12 (32-bit) / 24 (64-bit) (GCC)
static inline void deleter(ExternalRefCountData *self)
{
@@ -218,7 +233,7 @@ namespace QtSharedPointer {
executeDeleter(realself->extra.ptr, realself->extra.deleter);
// delete the deleter too
- realself->extra.~Next();
+ realself->extra.~CustomDeleter();
}
static void safetyCheckDeleter(ExternalRefCountData *self)
{
@@ -236,8 +251,8 @@ namespace QtSharedPointer {
Self *d = static_cast<Self *>(::operator new(sizeof(Self)));
// initialize the two sub-objects
- new (&d->extra) Next(ptr, userDeleter);
- new (d) Parent(destroy); // can't throw
+ new (&d->extra) CustomDeleter(ptr, userDeleter);
+ new (d) BaseClass(destroy); // can't throw
return d;
}
@@ -247,6 +262,10 @@ namespace QtSharedPointer {
~ExternalRefCountWithCustomDeleter();
};
+ // This class extends ExternalRefCountWithDestroyFn and adds a "T"
+ // member. That way, when the create() function is called, we allocate
+ // memory for both QSharedPointer's d-pointer and the actual object being
+ // tracked.
template <class T>
struct ExternalRefCountWithContiguousData: public ExternalRefCountWithDestroyFn
{
@@ -289,6 +308,8 @@ namespace QtSharedPointer {
~ExternalRefCountWithContiguousData();
};
+ // This is the main body of QSharedPointer. It implements the
+ // external reference counting functionality.
template <class T>
class ExternalRefCount: public Basic<T>
{