tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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