Bug 1476757 - Add methods to change the capacity of the ProfileBuffer. r=njn

Depends on D8218

Differential Revision: https://phabricator.services.mozilla.com/D6264

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Markus Stange 2018-10-12 13:39:47 +00:00
Родитель 6a39163ecd
Коммит b177228e70
2 изменённых файлов: 116 добавлений и 12 удалений

Просмотреть файл

@ -16,19 +16,14 @@
using namespace mozilla; using namespace mozilla;
ProfileBuffer::ProfileBuffer(uint32_t aCapacity) ProfileBuffer::ProfileBuffer(uint32_t aCapacity)
: mEntryIndexMask(0) : mEntries(nullptr)
, mEntryIndexMask(0)
, mRangeStart(0) , mRangeStart(0)
, mRangeEnd(0) , mRangeEnd(0)
, mCapacity(0) , mCapacity(0)
{ {
// Round aCapacity up to the nearest power of two, so that we can index bool succeeded = SetMinCapacity(aCapacity);
// mEntries with a simple mask and don't need to do a slow modulo operation. MOZ_RELEASE_ASSERT(succeeded, "Couldn't allocate initial ProfileBuffer storage");
const uint32_t UINT32_MAX_POWER_OF_TWO = 1 << 31;
MOZ_RELEASE_ASSERT(aCapacity <= UINT32_MAX_POWER_OF_TWO,
"aCapacity is larger than what we support");
mCapacity = RoundUpPow2(aCapacity);
mEntryIndexMask = mCapacity - 1;
mEntries = MakeUnique<ProfileBufferEntry[]>(mCapacity);
} }
ProfileBuffer::~ProfileBuffer() ProfileBuffer::~ProfileBuffer()
@ -38,6 +33,101 @@ ProfileBuffer::~ProfileBuffer()
} }
} }
bool
ProfileBuffer::SetMinCapacity(uint32_t aMinCapacity)
{
// Round aMinCapacity up to the nearest power of two, so that we can index
// mEntries with a simple mask and don't need to do a slow modulo operation.
const uint32_t UINT32_MAX_POWER_OF_TWO = 1 << 31;
MOZ_RELEASE_ASSERT(aMinCapacity <= UINT32_MAX_POWER_OF_TWO,
"aMinCapacity is larger than what we support");
return SetCapacityPow2(RoundUpPow2(aMinCapacity));
}
static uint64_t
RoundDownToMultipleOfPow2(uint64_t aNumber, uint64_t aMultiplier)
{
return aNumber & ~(aMultiplier - 1);
}
bool
ProfileBuffer::SetCapacityPow2(uint32_t aNewCapacity)
{
MOZ_RELEASE_ASSERT(aNewCapacity != 0, "can't set ProfileBuffer capacity to zero");
MOZ_RELEASE_ASSERT(IsPowerOfTwo(aNewCapacity), "aNewCapacity needs to be a power of two");
if (aNewCapacity == mCapacity) {
return true;
}
MOZ_RELEASE_ASSERT(Length() <= aNewCapacity, "can't make the capacity smaller than the used size");
auto newEntries = MakeUniqueFallible<ProfileBufferEntry[]>(aNewCapacity);
if (!newEntries) {
return false;
}
uint32_t newIndexMask = aNewCapacity - 1;
if (mCapacity != 0 && mRangeStart != mRangeEnd) {
// Copy existing data from mEntries into newEntries. Make sure that every
// entry preserves its position in buffer space.
// If the range wraps around in the old or in the new buffer, we need to
// copy the data in two chunks: [start, wrapIndex), [wrapIndex, end)
// If the range wraps in both the old and the new buffer, the wrap index
// will be the same in both buffers.
//
// If range doesn't wrap around:
//
// +- wrapIndex
// |+- mRangeStart
// || +- mRangeEnd
// vv v
// ...-+-----+-----+-----+-----+-----+-----+-----+-----+-...
// | |[---]| | | | | | |
// ...-+-----+-----+-----+-----+-----+-----+-----+-----+-...
// | [---] | |
// ...-+-----+-----+-----+-----+-----+-----+-----+-----+-...
// ^^^^^^^ smaller capacity
// ^^^^^^^^^^^^^^^^^^^^^^^^^ larger capacity
//
// If range wraps around:
//
// +- mRangeStart
// | +- wrapIndex
// | | +- mRangeEnd
// v v v
// ...-+-----+-----+-----+-----+-----+-----+-----+-----+-...
// | | | | [---+-] | | | |
// ...-+-----+-----+-----+-----+-----+-----+-----+-----+-...
// | [---+-] |
// ...-+-----+-----+-----+-----+-----+-----+-----+-----+-...
// ^^^^^^^ smaller capacity
// ^^^^^^^^^^^^^^^^^^^^^^^^^ larger capacity
uint64_t wrapIndex =
RoundDownToMultipleOfPow2(mRangeEnd, std::min(aNewCapacity, mCapacity));
if (wrapIndex <= mRangeStart) {
// There is no wrapping. Copy the entire range as one chunk.
PodCopy(&newEntries[mRangeStart & newIndexMask],
&mEntries[mRangeStart & mEntryIndexMask],
mRangeEnd - mRangeStart);
} else {
// Copy the range in two separate chunks.
PodCopy(&newEntries[mRangeStart & newIndexMask],
&mEntries[mRangeStart & mEntryIndexMask],
wrapIndex - mRangeStart);
PodCopy(&newEntries[wrapIndex & newIndexMask],
&mEntries[wrapIndex & mEntryIndexMask],
mRangeEnd - wrapIndex);
}
}
mCapacity = aNewCapacity;
mEntryIndexMask = newIndexMask;
mEntries = std::move(newEntries);
return true;
}
// Called from signal, call only reentrant functions // Called from signal, call only reentrant functions
void void
ProfileBuffer::AddEntry(const ProfileBufferEntry& aEntry) ProfileBuffer::AddEntry(const ProfileBufferEntry& aEntry)

Просмотреть файл

@ -13,9 +13,10 @@
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/RefCounted.h" #include "mozilla/RefCounted.h"
// A fixed-capacity circular buffer. // A resizeable circular buffer.
// This class is used as a queue of entries which, after construction, never // This class is used as a queue of entries which, outside of construction and
// allocates. This makes it safe to use in the profiler's "critical section". // calls to SetCapacity, never allocates. This makes it safe to use in the
// profiler's "critical section".
// Entries are appended at the end. Once the queue capacity has been reached, // Entries are appended at the end. Once the queue capacity has been reached,
// adding a new entry will evict an old entry from the start of the queue. // adding a new entry will evict an old entry from the start of the queue.
// Positions in the queue are represented as 64-bit unsigned integers which // Positions in the queue are represented as 64-bit unsigned integers which
@ -36,6 +37,19 @@ public:
~ProfileBuffer(); ~ProfileBuffer();
uint32_t Length() { return mRangeEnd - mRangeStart; }
// Set the buffer capacity to at least aMinCapacity. aMinCapacity must not be
// zero and at least Length(). Otherwise, it triggers abort. This method
// allocates. The allocation is fallible and the return value indicates success.
bool SetMinCapacity(uint32_t aMinCapacity);
// Set the buffer capacity to exactly aNewCapacity. aNewCapacity must be a
// power of two, non-zero, and at least Length(). Otherwise, it triggers abort.
// This method allocates. The allocation is fallible and the return value
// indicates success.
bool SetCapacityPow2(uint32_t aNewCapacity);
// Add |aEntry| to the buffer, ignoring what kind of entry it is. // Add |aEntry| to the buffer, ignoring what kind of entry it is.
void AddEntry(const ProfileBufferEntry& aEntry); void AddEntry(const ProfileBufferEntry& aEntry);