зеркало из https://github.com/mozilla/gecko-dev.git
170 строки
6.1 KiB
C++
170 строки
6.1 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef ProfileBufferChunkManagerSingle_h
|
|
#define ProfileBufferChunkManagerSingle_h
|
|
|
|
#include "mozilla/ProfileBufferChunkManager.h"
|
|
|
|
#ifdef DEBUG
|
|
# include "mozilla/Atomics.h"
|
|
#endif // DEBUG
|
|
|
|
namespace mozilla {
|
|
|
|
// Manages only one Chunk.
|
|
// The first call to `Get`/`RequestChunk()` will retrieve the one chunk, and all
|
|
// subsequent calls will return nullptr. That chunk may still be released, but
|
|
// it will never be destroyed or recycled.
|
|
// Unlike others, this manager may be `Reset()`, to allow another round of
|
|
// small-data gathering.
|
|
// The main use is with short-lived ProfileChunkedBuffers that collect little
|
|
// data that can fit in one chunk, e.g., capturing one stack.
|
|
// It is not thread-safe.
|
|
class ProfileBufferChunkManagerSingle final : public ProfileBufferChunkManager {
|
|
public:
|
|
using Length = ProfileBufferChunk::Length;
|
|
|
|
// Use a preallocated chunk. (Accepting null to gracefully handle OOM.)
|
|
explicit ProfileBufferChunkManagerSingle(UniquePtr<ProfileBufferChunk> aChunk)
|
|
: mInitialChunk(std::move(aChunk)),
|
|
mBufferBytes(mInitialChunk ? mInitialChunk->BufferBytes() : 0) {
|
|
MOZ_ASSERT(!mInitialChunk || !mInitialChunk->GetNext(),
|
|
"Expected at most one chunk");
|
|
}
|
|
|
|
// ChunkMinBufferBytes: Minimum number of user-available bytes in the Chunk.
|
|
// Note that Chunks use a bit more memory for their header.
|
|
explicit ProfileBufferChunkManagerSingle(Length aChunkMinBufferBytes)
|
|
: mInitialChunk(ProfileBufferChunk::Create(aChunkMinBufferBytes)),
|
|
mBufferBytes(mInitialChunk ? mInitialChunk->BufferBytes() : 0) {}
|
|
|
|
#ifdef DEBUG
|
|
~ProfileBufferChunkManagerSingle() { MOZ_ASSERT(mVirtuallyLocked == false); }
|
|
#endif // DEBUG
|
|
|
|
// Reset this manager, using the provided chunk (probably coming from the
|
|
// ProfileChunkedBuffer that just used it); if null, fallback on current or
|
|
// released chunk.
|
|
void Reset(UniquePtr<ProfileBufferChunk> aPossibleChunk) {
|
|
if (aPossibleChunk) {
|
|
mInitialChunk = std::move(aPossibleChunk);
|
|
mReleasedChunk = nullptr;
|
|
} else if (!mInitialChunk) {
|
|
MOZ_ASSERT(!!mReleasedChunk, "Can't reset properly!");
|
|
mInitialChunk = std::move(mReleasedChunk);
|
|
}
|
|
|
|
if (mInitialChunk) {
|
|
mInitialChunk->MarkRecycled();
|
|
mBufferBytes = mInitialChunk->BufferBytes();
|
|
} else {
|
|
mBufferBytes = 0;
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] size_t MaxTotalSize() const final { return mBufferBytes; }
|
|
|
|
// One of `GetChunk` and `RequestChunk` will only work the very first time (if
|
|
// there's even a chunk).
|
|
[[nodiscard]] UniquePtr<ProfileBufferChunk> GetChunk() final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
return std::move(mInitialChunk);
|
|
}
|
|
|
|
void RequestChunk(std::function<void(UniquePtr<ProfileBufferChunk>)>&&
|
|
aChunkReceiver) final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
// Simple retrieval.
|
|
std::move(aChunkReceiver)(GetChunk());
|
|
}
|
|
|
|
void FulfillChunkRequests() final {
|
|
// Nothing to do here.
|
|
}
|
|
|
|
void ReleaseChunks(UniquePtr<ProfileBufferChunk> aChunks) final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
MOZ_ASSERT(!mReleasedChunk, "Unexpected 2nd released chunk");
|
|
MOZ_ASSERT(!aChunks->GetNext(), "Only expected one released chunk");
|
|
mReleasedChunk = std::move(aChunks);
|
|
}
|
|
|
|
void SetChunkDestroyedCallback(
|
|
std::function<void(const ProfileBufferChunk&)>&& aChunkDestroyedCallback)
|
|
final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
// The chunk-destroyed callback will never actually be called, but we keep
|
|
// the callback here in case the caller expects it to live as long as this
|
|
// manager.
|
|
mChunkDestroyedCallback = std::move(aChunkDestroyedCallback);
|
|
}
|
|
|
|
[[nodiscard]] UniquePtr<ProfileBufferChunk> GetExtantReleasedChunks() final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
return std::move(mReleasedChunk);
|
|
}
|
|
|
|
void ForgetUnreleasedChunks() final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
}
|
|
|
|
[[nodiscard]] size_t SizeOfExcludingThis(
|
|
MallocSizeOf aMallocSizeOf) const final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
size_t size = 0;
|
|
if (mInitialChunk) {
|
|
size += mInitialChunk->SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
if (mReleasedChunk) {
|
|
size += mReleasedChunk->SizeOfIncludingThis(aMallocSizeOf);
|
|
}
|
|
// Note: Missing size of std::function external resources (if any).
|
|
return size;
|
|
}
|
|
|
|
[[nodiscard]] size_t SizeOfIncludingThis(
|
|
MallocSizeOf aMallocSizeOf) const final {
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
}
|
|
|
|
protected:
|
|
// This manager is not thread-safe, so there's not actual locking needed.
|
|
const ProfileBufferChunk* PeekExtantReleasedChunksAndLock() final {
|
|
MOZ_ASSERT(mVirtuallyLocked.compareExchange(false, true));
|
|
MOZ_ASSERT(mUser, "Not registered yet");
|
|
return mReleasedChunk.get();
|
|
}
|
|
void UnlockAfterPeekExtantReleasedChunks() final {
|
|
MOZ_ASSERT(mVirtuallyLocked.compareExchange(true, false));
|
|
}
|
|
|
|
private:
|
|
// Initial chunk created with this manager, given away at first Get/Request.
|
|
UniquePtr<ProfileBufferChunk> mInitialChunk;
|
|
|
|
// Storage for the released chunk (which should probably not happen, as it
|
|
// means the chunk is full).
|
|
UniquePtr<ProfileBufferChunk> mReleasedChunk;
|
|
|
|
// Size of the one chunk we're managing. Stored here, because the chunk may
|
|
// be moved out and inaccessible from here.
|
|
Length mBufferBytes;
|
|
|
|
// The chunk-destroyed callback will never actually be called, but we keep it
|
|
// here in case the caller expects it to live as long as this manager.
|
|
std::function<void(const ProfileBufferChunk&)> mChunkDestroyedCallback;
|
|
|
|
#ifdef DEBUG
|
|
mutable Atomic<bool> mVirtuallyLocked{false};
|
|
#endif // DEBUG
|
|
};
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // ProfileBufferChunkManagerSingle_h
|