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