Bug 877762 - GC: Post-barrier cycle collector participants - 2 Stop nsTArray memmoving Heap<T>s r=jlebar r=bz

This commit is contained in:
Jon Coppeard 2013-06-18 11:00:37 +01:00
parent 7295df4d69
commit 82a8b49403
2 changed files with 209 additions and 66 deletions

View File

@ -8,22 +8,22 @@
# error "Don't include this file directly"
#endif
template<class Alloc>
nsTArray_base<Alloc>::nsTArray_base()
template<class Alloc, class Copy>
nsTArray_base<Alloc, Copy>::nsTArray_base()
: mHdr(EmptyHdr()) {
MOZ_COUNT_CTOR(nsTArray_base);
}
template<class Alloc>
nsTArray_base<Alloc>::~nsTArray_base() {
template<class Alloc, class Copy>
nsTArray_base<Alloc, Copy>::~nsTArray_base() {
if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
Alloc::Free(mHdr);
}
MOZ_COUNT_DTOR(nsTArray_base);
}
template<class Alloc>
const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
template<class Alloc, class Copy>
const nsTArrayHeader* nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
// Assuming |this| points to an nsAutoArray, we want to get a pointer to
// mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf!
@ -48,8 +48,8 @@ const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elem
return reinterpret_cast<const Header*>(autoBuf);
}
template<class Alloc>
bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const {
template<class Alloc, class Copy>
bool nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const {
if (!mHdr->mIsAutoArray) {
return false;
}
@ -96,9 +96,9 @@ bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const {
}
template<class Alloc>
template<class Alloc, class Copy>
typename Alloc::ResultTypeProxy
nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) {
nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSize) {
// This should be the most common case so test this first
if (capacity <= mHdr->mCapacity)
return Alloc::SuccessResult();
@ -155,13 +155,16 @@ nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) {
}
Header *header;
if (UsesAutoArrayBuffer()) {
if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
// Malloc() and copy
header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc));
if (!header)
return Alloc::FailureResult();
memcpy(header, mHdr, sizeof(Header) + Length() * elemSize);
Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize);
if (!UsesAutoArrayBuffer())
Alloc::Free(mHdr);
} else {
// Realloc() existing data
header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc));
@ -179,9 +182,9 @@ nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) {
return Alloc::SuccessResult();
}
template<class Alloc>
template<class Alloc, class Copy>
void
nsTArray_base<Alloc>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
if (mHdr == EmptyHdr() || UsesAutoArrayBuffer())
return;
@ -195,7 +198,7 @@ nsTArray_base<Alloc>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
// Copy data, but don't copy the header to avoid overwriting mCapacity
header->mLength = length;
memcpy(header + 1, mHdr + 1, length * elemSize);
Copy::CopyElements(header + 1, mHdr + 1, length, elemSize);
Alloc::Free(mHdr);
mHdr = header;
@ -217,9 +220,9 @@ nsTArray_base<Alloc>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
mHdr->mCapacity = length;
}
template<class Alloc>
template<class Alloc, class Copy>
void
nsTArray_base<Alloc>::ShiftData(index_type start,
nsTArray_base<Alloc, Copy>::ShiftData(index_type start,
size_type oldLen, size_type newLen,
size_type elemSize, size_t elemAlign) {
if (oldLen == newLen)
@ -240,15 +243,14 @@ nsTArray_base<Alloc>::ShiftData(index_type start,
start *= elemSize;
newLen *= elemSize;
oldLen *= elemSize;
num *= elemSize;
char *base = reinterpret_cast<char*>(mHdr + 1) + start;
memmove(base + newLen, base + oldLen, num);
Copy::MoveElements(base + newLen, base + oldLen, num, elemSize);
}
}
template<class Alloc>
template<class Alloc, class Copy>
bool
nsTArray_base<Alloc>::InsertSlotsAt(index_type index, size_type count,
nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type index, size_type count,
size_type elementSize, size_t elemAlign) {
MOZ_ASSERT(index <= Length(), "Bogus insertion index");
size_type newLen = Length() + count;
@ -262,7 +264,7 @@ nsTArray_base<Alloc>::InsertSlotsAt(index_type index, size_type count,
// Move the existing elements as needed. Note that this will
// change our mLength, so no need to call IncrementLength.
ShiftData(index, 0, count, elementSize, elemAlign);
return true;
}
@ -274,18 +276,18 @@ nsTArray_base<Alloc>::InsertSlotsAt(index_type index, size_type count,
// * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr,
// array.mHdr points to array's auto buffer.
template<class Alloc>
nsTArray_base<Alloc>::IsAutoArrayRestorer::IsAutoArrayRestorer(
nsTArray_base<Alloc> &array,
size_t elemAlign)
template<class Alloc, class Copy>
nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer(
nsTArray_base<Alloc, Copy> &array,
size_t elemAlign)
: mArray(array),
mElemAlign(elemAlign),
mIsAuto(array.IsAutoArray())
{
}
template<class Alloc>
nsTArray_base<Alloc>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
template<class Alloc, class Copy>
nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
// Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr.
if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) {
// Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
@ -298,12 +300,11 @@ nsTArray_base<Alloc>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
}
}
template<class Alloc>
template<class Alloc, class Copy>
template<class Allocator>
typename Alloc::ResultTypeProxy
nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other,
size_type elemSize,
size_t elemAlign) {
nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator, Copy>& other,
size_type elemSize, size_t elemAlign) {
// EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an
// auto buffer. We need to point mHdr back to our auto buffer before we
@ -311,7 +312,7 @@ nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other,
// IsAutoArrayRestorer takes care of this for us.
IsAutoArrayRestorer ourAutoRestorer(*this, elemAlign);
typename nsTArray_base<Allocator>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign);
typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign);
// If neither array uses an auto buffer which is big enough to store the
// other array's elements, then ensure that both arrays use malloc'ed storage
@ -331,7 +332,7 @@ nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other,
return Alloc::SuccessResult();
}
// Swap the two arrays using memcpy, since at least one is using an auto
// Swap the two arrays by copying, since at least one is using an auto
// buffer which is large enough to hold all of the other's elements. We'll
// copy the shorter array into temporary storage.
//
@ -375,9 +376,9 @@ nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other,
return Alloc::FailureResult();
}
memcpy(temp.Elements(), smallerElements, smallerLength * elemSize);
memcpy(smallerElements, largerElements, largerLength * elemSize);
memcpy(largerElements, temp.Elements(), smallerLength * elemSize);
Copy::CopyElements(temp.Elements(), smallerElements, smallerLength, elemSize);
Copy::CopyElements(smallerElements, largerElements, largerLength, elemSize);
Copy::CopyElements(largerElements, temp.Elements(), smallerLength, elemSize);
// Swap the arrays' lengths.
NS_ABORT_IF_FALSE((other.Length() == 0 || mHdr != EmptyHdr()) &&
@ -390,9 +391,9 @@ nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other,
return Alloc::SuccessResult();
}
template<class Alloc>
template<class Alloc, class Copy>
bool
nsTArray_base<Alloc>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) {
nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) {
if (UsesAutoArrayBuffer()) {
// If you call this on a 0-length array, we'll set that array's mHdr to
@ -410,10 +411,10 @@ nsTArray_base<Alloc>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) {
if (!header)
return false;
memcpy(header, mHdr, size);
Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize);
header->mCapacity = Length();
mHdr = header;
}
return true;
}

View File

@ -22,6 +22,11 @@
#include "nsTraceRefcnt.h"
#include NEW_H
namespace JS {
template <class T>
class Heap;
} /* namespace JS */
//
// nsTArray is a resizable array class, like std::vector.
//
@ -56,6 +61,9 @@
// T MAY define operator< for sorting.
// T MAY define operator== for searching.
//
// (Note that the memmove requirement may be relaxed for certain types - see
// nsTArray_CopyElements below.)
//
// For methods taking a Comparator instance, the Comparator must be a class
// defining the following methods:
//
@ -342,13 +350,13 @@ struct nsTArray_SafeElementAtHelper<nsRefPtr<E>, Derived> :
// directly. It holds common implementation code that does not depend on the
// element type of the nsTArray.
//
template<class Alloc>
template<class Alloc, class Copy>
class nsTArray_base
{
// Allow swapping elements with |nsTArray_base|s created using a
// different allocator. This is kosher because all allocators use
// the same free().
template<class Allocator>
template<class Allocator, class Copier>
friend class nsTArray_base;
protected:
@ -396,7 +404,7 @@ protected:
// @param elemSize The size of an array element.
// @param elemAlign The alignment in bytes of an array element.
void ShrinkCapacity(size_type elemSize, size_t elemAlign);
// This method may be called to resize a "gap" in the array by shifting
// elements around. It updates mLength appropriately. If the resulting
// array has zero elements, then the array's memory is free'd.
@ -435,18 +443,18 @@ protected:
protected:
template<class Allocator>
typename Alloc::ResultTypeProxy
SwapArrayElements(nsTArray_base<Allocator>& other,
SwapArrayElements(nsTArray_base<Allocator, Copy>& other,
size_type elemSize,
size_t elemAlign);
// This is an RAII class used in SwapArrayElements.
class IsAutoArrayRestorer {
public:
IsAutoArrayRestorer(nsTArray_base<Alloc> &array, size_t elemAlign);
IsAutoArrayRestorer(nsTArray_base<Alloc, Copy> &array, size_t elemAlign);
~IsAutoArrayRestorer();
private:
nsTArray_base<Alloc> &mArray;
nsTArray_base<Alloc, Copy> &mArray;
size_t mElemAlign;
bool mIsAuto;
};
@ -473,7 +481,7 @@ protected:
// Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't
// assert that we are an nsAutoTArray.
Header* GetAutoArrayBufferUnsafe(size_t elemAlign) {
return const_cast<Header*>(static_cast<const nsTArray_base<Alloc>*>(this)->
return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(this)->
GetAutoArrayBufferUnsafe(elemAlign));
}
const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const;
@ -486,7 +494,7 @@ protected:
// null. If the array is empty, then this will point to sEmptyHdr.
Header *mHdr;
Header* Hdr() const {
Header* Hdr() const {
return mHdr;
}
@ -563,6 +571,139 @@ struct AssignRangeAlgorithm<true, true> {
}
};
//
// Normally elements are copied with memcpy and memmove, but for some element
// types that is problematic. The nsTArray_CopyElements template class can be
// specialized to ensure that copying calls constructors and destructors
// instead, as is done below for JS::Heap<E> elements.
//
//
// A class that defines how to copy elements using memcpy/memmove.
//
struct nsTArray_CopyWithMemutils
{
const static bool allowRealloc = true;
static void CopyElements(void* dest, const void* src, size_t count, size_t elemSize) {
memcpy(dest, src, count * elemSize);
}
static void CopyHeaderAndElements(void* dest, const void* src, size_t count, size_t elemSize) {
memcpy(dest, src, sizeof(nsTArrayHeader) + count * elemSize);
}
static void MoveElements(void* dest, const void* src, size_t count, size_t elemSize) {
memmove(dest, src, count * elemSize);
}
};
//
// A template class that defines how to copy elements calling their constructors
// and destructors appropriately.
//
template <class ElemType>
struct nsTArray_CopyWithConstructors
{
typedef nsTArrayElementTraits<ElemType> traits;
const static bool allowRealloc = false;
static void CopyElements(void* dest, void* src, size_t count, size_t elemSize) {
ElemType* destElem = static_cast<ElemType*>(dest);
ElemType* srcElem = static_cast<ElemType*>(src);
ElemType* destElemEnd = destElem + count;
#ifdef DEBUG
ElemType* srcElemEnd = srcElem + count;
MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd);
#endif
while (destElem != destElemEnd) {
traits::Construct(destElem, *srcElem);
traits::Destruct(srcElem);
++destElem;
++srcElem;
}
}
static void CopyHeaderAndElements(void* dest, void* src, size_t count, size_t elemSize) {
nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(dest);
nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(src);
*destHeader = *srcHeader;
CopyElements(static_cast<uint8_t*>(dest) + sizeof(nsTArrayHeader),
static_cast<uint8_t*>(src) + sizeof(nsTArrayHeader),
count, elemSize);
}
static void MoveElements(void* dest, void* src, size_t count, size_t elemSize) {
ElemType* destElem = static_cast<ElemType*>(dest);
ElemType* srcElem = static_cast<ElemType*>(src);
ElemType* destElemEnd = destElem + count;
ElemType* srcElemEnd = srcElem + count;
if (destElem == srcElem) {
return; // In practice, we don't do this.
} else if (srcElemEnd > destElem && srcElemEnd < destElemEnd) {
while (destElemEnd != destElem) {
--destElemEnd;
--srcElemEnd;
traits::Construct(destElemEnd, *srcElemEnd);
traits::Destruct(srcElem);
}
} else {
CopyElements(dest, src, count, elemSize);
}
}
};
//
// The default behaviour is to use memcpy/memmove for everything.
//
template <class E>
struct nsTArray_CopyElements : public nsTArray_CopyWithMemutils {};
//
// JS::Heap<E> elements require constructors/destructors to be called and so is
// specialized here.
//
template <class E>
struct nsTArray_CopyElements<JS::Heap<E> > : public nsTArray_CopyWithConstructors<E> {};
//
// Base class for nsTArray_Impl that is templated on element type and derived
// nsTArray_Impl class, to allow extra conversions to be added for specific
// types.
//
template <class E, class Derived>
struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived> {};
//
// Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E>
// elements.
//
// These conversions are safe because JS::Heap<E> and E share the same
// representation, and since the result of the conversions are const references
// we won't miss any barriers.
//
// The static_cast is necessary to obtain the correct address for the derived
// class since we are a base class used in multiple inheritance.
//
template <class E, class Derived>
struct nsTArray_TypedBase<JS::Heap<E>, Derived>
: public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived>
{
operator const nsTArray<E>& () {
MOZ_STATIC_ASSERT(sizeof(E) == sizeof(JS::Heap<E>),
"JS::Heap<E> must be binary compatible with E.");
Derived* self = static_cast<Derived*>(this);
return *reinterpret_cast<nsTArray<E> *>(self);
}
operator const FallibleTArray<E>& () {
Derived* self = static_cast<Derived*>(this);
return *reinterpret_cast<FallibleTArray<E> *>(self);
}
};
//
// nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray,
// nsAutoTArray, and AutoFallibleTArray.
@ -576,16 +717,17 @@ struct AssignRangeAlgorithm<true, true> {
// TArrays can be cast to |const nsTArray&|.
//
template<class E, class Alloc>
class nsTArray_Impl : public nsTArray_base<Alloc>,
public nsTArray_SafeElementAtHelper<E, nsTArray_Impl<E, Alloc> >
class nsTArray_Impl : public nsTArray_base<Alloc, nsTArray_CopyElements<E> >,
public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc> >
{
public:
typedef nsTArray_base<Alloc> base_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::index_type index_type;
typedef E elem_type;
typedef nsTArray_Impl<E, Alloc> self_type;
typedef nsTArrayElementTraits<E> elem_traits;
typedef nsTArray_CopyElements<E> copy_type;
typedef nsTArray_base<Alloc, copy_type> base_type;
typedef typename base_type::size_type size_type;
typedef typename base_type::index_type index_type;
typedef E elem_type;
typedef nsTArray_Impl<E, Alloc> self_type;
typedef nsTArrayElementTraits<E> elem_traits;
typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type;
using safeelementat_helper_type::SafeElementAt;
@ -716,7 +858,7 @@ public:
const elem_type* Elements() const {
return reinterpret_cast<const elem_type *>(Hdr() + 1);
}
// This method provides direct access to the i'th element of the array.
// The given index must be within the array bounds.
// @param i The index of an element in the array.
@ -1083,7 +1225,7 @@ public:
return AppendElements(1);
}
// Move all elements from another array to the end of this array without
// Move all elements from another array to the end of this array without
// calling copy constructors or destructors.
// @return A pointer to the newly appended elements, or null on OOM.
template<class Item, class Allocator>
@ -1093,8 +1235,8 @@ public:
index_type otherLen = array.Length();
if (!Alloc::Successful(this->EnsureCapacity(len + otherLen, sizeof(elem_type))))
return nullptr;
memcpy(Elements() + len, array.Elements(), otherLen * sizeof(elem_type));
this->IncrementLength(otherLen);
copy_type::CopyElements(Elements() + len, array.Elements(), otherLen, sizeof(elem_type));
this->IncrementLength(otherLen);
array.ShiftData(0, otherLen, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type));
return Elements() + len;
}
@ -1201,7 +1343,7 @@ public:
if (newLen > oldLen) {
return InsertElementsAt(oldLen, newLen - oldLen) != nullptr;
}
TruncateLength(newLen);
return true;
}
@ -1283,7 +1425,7 @@ typename Alloc::ResultType EnsureLengthAtLeast(size_type minLen) {
//
// Sorting
//
// This function is meant to be used with the NS_QuickSort function. It
// maps the callback API expected by NS_QuickSort to the Comparator API
// used by nsTArray_Impl. See nsTArray_Impl::Sort.
@ -1396,8 +1538,8 @@ protected:
// This method invokes elem_type's copy-constructor on a range of elements.
// @param start The index of the first element to construct.
// @param count The number of elements to construct.
// @param values The array of elements to copy.
// @param count The number of elements to construct.
// @param values The array of elements to copy.
template<class Item>
void AssignRange(index_type start, size_type count,
const Item *values) {
@ -1533,7 +1675,7 @@ protected:
private:
// nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer
// to mAutoBuf.
template<class Allocator>
template<class Allocator, class Copier>
friend class nsTArray_base;
void Init() {