tor-browser

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

ShmemPool.h (5594B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et ft=cpp : */
      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_ShmemPool_h
      8 #define mozilla_ShmemPool_h
      9 
     10 #include "mozilla/Mutex.h"
     11 #include "mozilla/ipc/Shmem.h"
     12 #include "nsTArray.h"
     13 
     14 extern mozilla::LazyLogModule sShmemPoolLog;
     15 #define SHMEMPOOL_LOG(args) \
     16  MOZ_LOG(sShmemPoolLog, mozilla::LogLevel::Debug, args)
     17 #define SHMEMPOOL_LOG_WARN(args) \
     18  MOZ_LOG(sShmemPoolLog, mozilla::LogLevel::Warning, args)
     19 #define SHMEMPOOL_LOG_ERROR(args) \
     20  MOZ_LOG(sShmemPoolLog, mozilla::LogLevel::Error, args)
     21 
     22 namespace mozilla {
     23 
     24 class ShmemPool;
     25 
     26 class ShmemBuffer {
     27 public:
     28  ShmemBuffer() : mInitialized(false) {}
     29  explicit ShmemBuffer(mozilla::ipc::Shmem aShmem) {
     30    mInitialized = true;
     31    mShmem = aShmem;
     32  }
     33 
     34  ShmemBuffer(ShmemBuffer&& rhs) {
     35    mInitialized = rhs.mInitialized;
     36    mShmem = std::move(rhs.mShmem);
     37  }
     38 
     39  ShmemBuffer& operator=(ShmemBuffer&& rhs) {
     40    MOZ_ASSERT(&rhs != this, "self-moves are prohibited");
     41    mInitialized = rhs.mInitialized;
     42    mShmem = std::move(rhs.mShmem);
     43    return *this;
     44  }
     45 
     46  // No copies allowed
     47  ShmemBuffer(const ShmemBuffer&) = delete;
     48  ShmemBuffer& operator=(const ShmemBuffer&) = delete;
     49 
     50  bool Valid() { return mInitialized; }
     51 
     52  uint8_t* GetBytes() { return mShmem.get<uint8_t>(); }
     53 
     54  mozilla::ipc::Shmem& Get() { return mShmem; }
     55 
     56 private:
     57  friend class ShmemPool;
     58 
     59  bool mInitialized;
     60  mozilla::ipc::Shmem mShmem;
     61 };
     62 
     63 class ShmemPool final {
     64 public:
     65  enum class PoolType { StaticPool, DynamicPool };
     66  explicit ShmemPool(size_t aPoolSize,
     67                     PoolType aPoolType = PoolType::StaticPool);
     68  ~ShmemPool();
     69  // Get/GetIfAvailable differ in what thread they can run on. GetIfAvailable
     70  // can run anywhere but won't allocate if the right size isn't available.
     71  ShmemBuffer GetIfAvailable(size_t aSize);
     72  void Put(ShmemBuffer&& aShmem);
     73 
     74  // We need to use the allocation/deallocation functions
     75  // of a specific IPC child/parent instance.
     76  template <class T>
     77  void Cleanup(T* aInstance) {
     78    MutexAutoLock lock(mMutex);
     79    for (size_t i = 0; i < mShmemPool.Length(); i++) {
     80      if (mShmemPool[i].mInitialized) {
     81        aInstance->DeallocShmem(mShmemPool[i].Get());
     82        mShmemPool[i].mInitialized = false;
     83      }
     84    }
     85  }
     86 
     87  enum class AllocationPolicy { Default, Unsafe };
     88 
     89  template <class T>
     90  ShmemBuffer Get(T* aInstance, size_t aSize,
     91                  AllocationPolicy aPolicy = AllocationPolicy::Default) {
     92    MutexAutoLock lock(mMutex);
     93 
     94    // Pool is empty, don't block caller.
     95    if (mPoolFree == 0 && mPoolType == PoolType::StaticPool) {
     96      if (!mErrorLogged) {
     97        // log "out of pool" once as error to avoid log spam
     98        mErrorLogged = true;
     99        SHMEMPOOL_LOG_ERROR(
    100            ("ShmemPool is empty, future occurrences "
    101             "will be logged as warnings"));
    102      } else {
    103        SHMEMPOOL_LOG_WARN(("ShmemPool is empty"));
    104      }
    105      // This isn't initialized, so will be understood as an error.
    106      return ShmemBuffer();
    107    }
    108    if (mPoolFree == 0) {
    109      MOZ_ASSERT(mPoolType == PoolType::DynamicPool);
    110      SHMEMPOOL_LOG(("Dynamic ShmemPool empty, allocating extra Shmem buffer"));
    111      ShmemBuffer newBuffer;
    112      mShmemPool.InsertElementAt(0, std::move(newBuffer));
    113      mPoolFree++;
    114    }
    115 
    116    ShmemBuffer& res = mShmemPool[mPoolFree - 1];
    117 
    118    if (!res.mInitialized) {
    119      SHMEMPOOL_LOG(("Initializing new Shmem in pool"));
    120      if (!AllocateShmem(aInstance, aSize, res, aPolicy)) {
    121        SHMEMPOOL_LOG(("Failure allocating new Shmem buffer"));
    122        return ShmemBuffer();
    123      }
    124      res.mInitialized = true;
    125    }
    126 
    127    MOZ_DIAGNOSTIC_ASSERT(res.mShmem.IsWritable(),
    128                          "Shmem in Pool is not writable?");
    129 
    130    // Prepare buffer, increase size if needed (we never shrink as we don't
    131    // maintain seperate sized pools and we don't want to keep reallocating)
    132    if (res.mShmem.Size<char>() < aSize) {
    133      SHMEMPOOL_LOG(("Size change/increase in Shmem Pool"));
    134      aInstance->DeallocShmem(res.mShmem);
    135      res.mInitialized = false;
    136      // this may fail; always check return value
    137      if (!AllocateShmem(aInstance, aSize, res, aPolicy)) {
    138        SHMEMPOOL_LOG(("Failure allocating resized Shmem buffer"));
    139        return ShmemBuffer();
    140      } else {
    141        res.mInitialized = true;
    142      }
    143    }
    144 
    145    MOZ_ASSERT(res.mShmem.IsWritable(),
    146               "Shmem in Pool is not writable post resize?");
    147 
    148    mPoolFree--;
    149 #ifdef DEBUG
    150    size_t poolUse = mShmemPool.Length() - mPoolFree;
    151    if (poolUse > mMaxPoolUse) {
    152      mMaxPoolUse = poolUse;
    153      SHMEMPOOL_LOG(
    154          ("Maximum ShmemPool use increased: %zu buffers", mMaxPoolUse));
    155    }
    156 #endif
    157    return std::move(res);
    158  }
    159 
    160 private:
    161  template <class T>
    162  bool AllocateShmem(T* aInstance, size_t aSize, ShmemBuffer& aRes,
    163                     AllocationPolicy aPolicy) {
    164    return (aPolicy == AllocationPolicy::Default &&
    165            aInstance->AllocShmem(aSize, &aRes.mShmem)) ||
    166           (aPolicy == AllocationPolicy::Unsafe &&
    167            aInstance->AllocUnsafeShmem(aSize, &aRes.mShmem));
    168  }
    169  const PoolType mPoolType;
    170  Mutex mMutex MOZ_UNANNOTATED;
    171  size_t mPoolFree;
    172  bool mErrorLogged;
    173 #ifdef DEBUG
    174  size_t mMaxPoolUse;
    175 #endif
    176  nsTArray<ShmemBuffer> mShmemPool;
    177 };
    178 
    179 }  // namespace mozilla
    180 
    181 #endif  // mozilla_ShmemPool_h