tor-browser

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

ProfileBufferControlledChunkManager.h (8271B)


      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 ProfileBufferControlledChunkManager_h
      8 #define ProfileBufferControlledChunkManager_h
      9 
     10 #include "mozilla/ProfileBufferChunk.h"
     11 
     12 #include <functional>
     13 #include <vector>
     14 
     15 namespace mozilla {
     16 
     17 // A "Controlled" chunk manager will provide updates about chunks that it
     18 // creates, releases, and destroys; and it can destroy released chunks as
     19 // requested.
     20 class ProfileBufferControlledChunkManager {
     21 public:
     22  using Length = ProfileBufferChunk::Length;
     23 
     24  virtual ~ProfileBufferControlledChunkManager() = default;
     25 
     26  // Minimum amount of chunk metadata to be transferred between processes.
     27  struct ChunkMetadata {
     28    // Timestamp when chunk was marked "done", which is used to:
     29    // - determine its age, so the oldest one will be destroyed first,
     30    // - uniquely identify this chunk in this process. (The parent process is
     31    //   responsible for associating this timestamp to its process id.)
     32    TimeStamp mDoneTimeStamp;
     33    // Size of this chunk's buffer.
     34    Length mBufferBytes;
     35 
     36    ChunkMetadata(TimeStamp aDoneTimeStamp, Length aBufferBytes)
     37        : mDoneTimeStamp(aDoneTimeStamp), mBufferBytes(aBufferBytes) {}
     38  };
     39 
     40  // Class collecting all information necessary to describe updates that
     41  // happened in a chunk manager.
     42  // An update can be folded into a previous update.
     43  class Update {
     44   public:
     45    // Construct a "not-an-Update" object, which should only be used after a
     46    // real update is folded into it.
     47    Update() = default;
     48 
     49    // Construct a "final" Update, which marks the end of all updates from a
     50    // chunk manager.
     51    explicit Update(decltype(nullptr)) : mUnreleasedBytes(FINAL) {}
     52 
     53    // Construct an Update from the given data and released chunks.
     54    // The chunk pointers may be null, and it doesn't matter if
     55    // `aNewlyReleasedChunks` is already linked to `aExistingReleasedChunks` or
     56    // not.
     57    Update(size_t aUnreleasedBytes, size_t aReleasedBytes,
     58           const ProfileBufferChunk* aExistingReleasedChunks,
     59           const ProfileBufferChunk* aNewlyReleasedChunks)
     60        : mUnreleasedBytes(aUnreleasedBytes),
     61          mReleasedBytes(aReleasedBytes),
     62          mOldestDoneTimeStamp(
     63              aExistingReleasedChunks
     64                  ? aExistingReleasedChunks->ChunkHeader().mDoneTimeStamp
     65                  : TimeStamp{}) {
     66      MOZ_RELEASE_ASSERT(
     67          !IsNotUpdate(),
     68          "Empty update should only be constructed with default constructor");
     69      MOZ_RELEASE_ASSERT(
     70          !IsFinal(),
     71          "Final update should only be constructed with nullptr constructor");
     72      for (const ProfileBufferChunk* chunk = aNewlyReleasedChunks; chunk;
     73           chunk = chunk->GetNext()) {
     74        mNewlyReleasedChunks.emplace_back(ChunkMetadata{
     75            chunk->ChunkHeader().mDoneTimeStamp, chunk->BufferBytes()});
     76      }
     77    }
     78 
     79    // Construct an Update from raw data.
     80    // This may be used to re-construct an Update that was previously
     81    // serialized.
     82    Update(size_t aUnreleasedBytes, size_t aReleasedBytes,
     83           TimeStamp aOldestDoneTimeStamp,
     84           std::vector<ChunkMetadata>&& aNewlyReleasedChunks)
     85        : mUnreleasedBytes(aUnreleasedBytes),
     86          mReleasedBytes(aReleasedBytes),
     87          mOldestDoneTimeStamp(aOldestDoneTimeStamp),
     88          mNewlyReleasedChunks(std::move(aNewlyReleasedChunks)) {}
     89 
     90    // Clear the Update completely and return it to a "not-an-Update" state.
     91    void Clear() {
     92      mUnreleasedBytes = NO_UPDATE;
     93      mReleasedBytes = 0;
     94      mOldestDoneTimeStamp = TimeStamp{};
     95      mNewlyReleasedChunks.clear();
     96    }
     97 
     98    bool IsNotUpdate() const { return mUnreleasedBytes == NO_UPDATE; }
     99 
    100    bool IsFinal() const { return mUnreleasedBytes == FINAL; }
    101 
    102    size_t UnreleasedBytes() const {
    103      MOZ_RELEASE_ASSERT(!IsNotUpdate(),
    104                         "Cannot access UnreleasedBytes from empty update");
    105      MOZ_RELEASE_ASSERT(!IsFinal(),
    106                         "Cannot access UnreleasedBytes from final update");
    107      return mUnreleasedBytes;
    108    }
    109 
    110    size_t ReleasedBytes() const {
    111      MOZ_RELEASE_ASSERT(!IsNotUpdate(),
    112                         "Cannot access ReleasedBytes from empty update");
    113      MOZ_RELEASE_ASSERT(!IsFinal(),
    114                         "Cannot access ReleasedBytes from final update");
    115      return mReleasedBytes;
    116    }
    117 
    118    TimeStamp OldestDoneTimeStamp() const {
    119      MOZ_RELEASE_ASSERT(!IsNotUpdate(),
    120                         "Cannot access OldestDoneTimeStamp from empty update");
    121      MOZ_RELEASE_ASSERT(!IsFinal(),
    122                         "Cannot access OldestDoneTimeStamp from final update");
    123      return mOldestDoneTimeStamp;
    124    }
    125 
    126    const std::vector<ChunkMetadata>& NewlyReleasedChunksRef() const {
    127      MOZ_RELEASE_ASSERT(
    128          !IsNotUpdate(),
    129          "Cannot access NewlyReleasedChunksRef from empty update");
    130      MOZ_RELEASE_ASSERT(
    131          !IsFinal(), "Cannot access NewlyReleasedChunksRef from final update");
    132      return mNewlyReleasedChunks;
    133    }
    134 
    135    // Fold a later update into this one.
    136    void Fold(Update&& aNewUpdate) {
    137      MOZ_ASSERT(
    138          !IsFinal() || aNewUpdate.IsFinal(),
    139          "There shouldn't be another non-final update after the final update");
    140 
    141      if (IsNotUpdate() || aNewUpdate.IsFinal()) {
    142        // We were empty, or the new update is the final update, we just switch
    143        // to that new update.
    144        *this = std::move(aNewUpdate);
    145        return;
    146      }
    147 
    148      mUnreleasedBytes = aNewUpdate.mUnreleasedBytes;
    149      mReleasedBytes = aNewUpdate.mReleasedBytes;
    150      if (!aNewUpdate.mOldestDoneTimeStamp.IsNull()) {
    151        MOZ_ASSERT(mOldestDoneTimeStamp.IsNull() ||
    152                   mOldestDoneTimeStamp <= aNewUpdate.mOldestDoneTimeStamp);
    153        mOldestDoneTimeStamp = aNewUpdate.mOldestDoneTimeStamp;
    154        auto it = mNewlyReleasedChunks.begin();
    155        while (it != mNewlyReleasedChunks.end() &&
    156               it->mDoneTimeStamp < mOldestDoneTimeStamp) {
    157          it = mNewlyReleasedChunks.erase(it);
    158        }
    159      }
    160      if (!aNewUpdate.mNewlyReleasedChunks.empty()) {
    161        mNewlyReleasedChunks.reserve(mNewlyReleasedChunks.size() +
    162                                     aNewUpdate.mNewlyReleasedChunks.size());
    163        mNewlyReleasedChunks.insert(mNewlyReleasedChunks.end(),
    164                                    aNewUpdate.mNewlyReleasedChunks.begin(),
    165                                    aNewUpdate.mNewlyReleasedChunks.end());
    166      }
    167    }
    168 
    169   private:
    170    static const size_t NO_UPDATE = size_t(-1);
    171    static const size_t FINAL = size_t(-2);
    172 
    173    size_t mUnreleasedBytes = NO_UPDATE;
    174    size_t mReleasedBytes = 0;
    175    TimeStamp mOldestDoneTimeStamp;
    176    std::vector<ChunkMetadata> mNewlyReleasedChunks;
    177  };
    178 
    179  using UpdateCallback = std::function<void(Update&&)>;
    180 
    181  // This *may* be set (or reset) by an object that needs to know about all
    182  // chunk updates that happen in this manager. The main use will be to
    183  // coordinate the global memory usage of Firefox.
    184  // If a non-empty callback is given, it will be immediately invoked with the
    185  // current state.
    186  // When the callback is about to be destroyed (by overwriting it here, or in
    187  // the class destructor), it will be invoked one last time with an empty
    188  // update.
    189  // Note that the callback (even the first current-state callback) will be
    190  // invoked from inside a locked scope, so it should *not* call other functions
    191  // of the chunk manager. A side benefit of this locking is that it guarantees
    192  // that no two invocations can overlap.
    193  virtual void SetUpdateCallback(UpdateCallback&& aUpdateCallback) = 0;
    194 
    195  // This is a request to destroy all chunks before the given timestamp.
    196  // This timestamp should be one that was given in a previous UpdateCallback
    197  // call. Obviously, only released chunks can be destroyed.
    198  virtual void DestroyChunksAtOrBefore(TimeStamp aDoneTimeStamp) = 0;
    199 };
    200 
    201 }  // namespace mozilla
    202 
    203 #endif  // ProfileBufferControlledChunkManager_h