ProfileBufferChunkManagerSingle.h (6240B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef ProfileBufferChunkManagerSingle_h 8 #define ProfileBufferChunkManagerSingle_h 9 10 #include "mozilla/ProfileBufferChunkManager.h" 11 12 #ifdef DEBUG 13 # include "mozilla/Atomics.h" 14 #endif // DEBUG 15 16 namespace mozilla { 17 18 // Manages only one Chunk. 19 // The first call to `Get`/`RequestChunk()` will retrieve the one chunk, and all 20 // subsequent calls will return nullptr. That chunk may still be released, but 21 // it will never be destroyed or recycled. 22 // Unlike others, this manager may be `Reset()`, to allow another round of 23 // small-data gathering. 24 // The main use is with short-lived ProfileChunkedBuffers that collect little 25 // data that can fit in one chunk, e.g., capturing one stack. 26 // It is not thread-safe. 27 class ProfileBufferChunkManagerSingle final : public ProfileBufferChunkManager { 28 public: 29 using Length = ProfileBufferChunk::Length; 30 31 // Use a preallocated chunk. (Accepting null to gracefully handle OOM.) 32 explicit ProfileBufferChunkManagerSingle(UniquePtr<ProfileBufferChunk> aChunk) 33 : mInitialChunk(std::move(aChunk)), 34 mBufferBytes(mInitialChunk ? mInitialChunk->BufferBytes() : 0) { 35 MOZ_ASSERT(!mInitialChunk || !mInitialChunk->GetNext(), 36 "Expected at most one chunk"); 37 } 38 39 // ChunkMinBufferBytes: Minimum number of user-available bytes in the Chunk. 40 // Note that Chunks use a bit more memory for their header. 41 explicit ProfileBufferChunkManagerSingle(Length aChunkMinBufferBytes) 42 : mInitialChunk(ProfileBufferChunk::Create(aChunkMinBufferBytes)), 43 mBufferBytes(mInitialChunk ? mInitialChunk->BufferBytes() : 0) {} 44 45 #ifdef DEBUG 46 ~ProfileBufferChunkManagerSingle() { MOZ_ASSERT(mVirtuallyLocked == false); } 47 #endif // DEBUG 48 49 // Reset this manager, using the provided chunk (probably coming from the 50 // ProfileChunkedBuffer that just used it); if null, fallback on current or 51 // released chunk. 52 void Reset(UniquePtr<ProfileBufferChunk> aPossibleChunk) { 53 if (aPossibleChunk) { 54 mInitialChunk = std::move(aPossibleChunk); 55 mReleasedChunk = nullptr; 56 } else if (!mInitialChunk) { 57 MOZ_ASSERT(!!mReleasedChunk, "Can't reset properly!"); 58 mInitialChunk = std::move(mReleasedChunk); 59 } 60 61 if (mInitialChunk) { 62 mInitialChunk->MarkRecycled(); 63 mBufferBytes = mInitialChunk->BufferBytes(); 64 } else { 65 mBufferBytes = 0; 66 } 67 } 68 69 [[nodiscard]] size_t MaxTotalSize() const final { return mBufferBytes; } 70 71 // One of `GetChunk` and `RequestChunk` will only work the very first time (if 72 // there's even a chunk). 73 [[nodiscard]] UniquePtr<ProfileBufferChunk> GetChunk() final { 74 MOZ_ASSERT(mUser, "Not registered yet"); 75 return std::move(mInitialChunk); 76 } 77 78 void RequestChunk(std::function<void(UniquePtr<ProfileBufferChunk>)>&& 79 aChunkReceiver) final { 80 MOZ_ASSERT(mUser, "Not registered yet"); 81 // Simple retrieval. 82 std::move(aChunkReceiver)(GetChunk()); 83 } 84 85 void FulfillChunkRequests() final { 86 // Nothing to do here. 87 } 88 89 void ReleaseChunk(UniquePtr<ProfileBufferChunk> aChunk) final { 90 MOZ_ASSERT(mUser, "Not registered yet"); 91 if (!aChunk) { 92 return; 93 } 94 MOZ_ASSERT(!mReleasedChunk, "Unexpected 2nd released chunk"); 95 MOZ_ASSERT(!aChunk->GetNext(), "Only expected one released chunk"); 96 mReleasedChunk = std::move(aChunk); 97 } 98 99 void SetChunkDestroyedCallback( 100 std::function<void(const ProfileBufferChunk&)>&& aChunkDestroyedCallback) 101 final { 102 MOZ_ASSERT(mUser, "Not registered yet"); 103 // The chunk-destroyed callback will never actually be called, but we keep 104 // the callback here in case the caller expects it to live as long as this 105 // manager. 106 mChunkDestroyedCallback = std::move(aChunkDestroyedCallback); 107 } 108 109 [[nodiscard]] UniquePtr<ProfileBufferChunk> GetExtantReleasedChunks() final { 110 MOZ_ASSERT(mUser, "Not registered yet"); 111 return std::move(mReleasedChunk); 112 } 113 114 void ForgetUnreleasedChunks() final { 115 MOZ_ASSERT(mUser, "Not registered yet"); 116 } 117 118 [[nodiscard]] size_t SizeOfExcludingThis( 119 MallocSizeOf aMallocSizeOf) const final { 120 MOZ_ASSERT(mUser, "Not registered yet"); 121 size_t size = 0; 122 if (mInitialChunk) { 123 size += mInitialChunk->SizeOfIncludingThis(aMallocSizeOf); 124 } 125 if (mReleasedChunk) { 126 size += mReleasedChunk->SizeOfIncludingThis(aMallocSizeOf); 127 } 128 // Note: Missing size of std::function external resources (if any). 129 return size; 130 } 131 132 [[nodiscard]] size_t SizeOfIncludingThis( 133 MallocSizeOf aMallocSizeOf) const final { 134 MOZ_ASSERT(mUser, "Not registered yet"); 135 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 136 } 137 138 protected: 139 // This manager is not thread-safe, so there's not actual locking needed. 140 const ProfileBufferChunk* PeekExtantReleasedChunksAndLock() final { 141 MOZ_ASSERT(mVirtuallyLocked.compareExchange(false, true)); 142 MOZ_ASSERT(mUser, "Not registered yet"); 143 return mReleasedChunk.get(); 144 } 145 void UnlockAfterPeekExtantReleasedChunks() final { 146 MOZ_ASSERT(mVirtuallyLocked.compareExchange(true, false)); 147 } 148 149 private: 150 // Initial chunk created with this manager, given away at first Get/Request. 151 UniquePtr<ProfileBufferChunk> mInitialChunk; 152 153 // Storage for the released chunk (which should probably not happen, as it 154 // means the chunk is full). 155 UniquePtr<ProfileBufferChunk> mReleasedChunk; 156 157 // Size of the one chunk we're managing. Stored here, because the chunk may 158 // be moved out and inaccessible from here. 159 Length mBufferBytes; 160 161 // The chunk-destroyed callback will never actually be called, but we keep it 162 // here in case the caller expects it to live as long as this manager. 163 std::function<void(const ProfileBufferChunk&)> mChunkDestroyedCallback; 164 165 #ifdef DEBUG 166 mutable Atomic<bool> mVirtuallyLocked{false}; 167 #endif // DEBUG 168 }; 169 170 } // namespace mozilla 171 172 #endif // ProfileBufferChunkManagerSingle_h