Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / systemservices / ShmemPool.h
blob4a08f0892b632ca5b8194a937f63100d537649ac
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/. */
7 #ifndef mozilla_ShmemPool_h
8 #define mozilla_ShmemPool_h
10 #include "mozilla/Mutex.h"
11 #include "mozilla/ipc/Shmem.h"
12 #include "nsTArray.h"
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)
22 namespace mozilla {
24 class ShmemPool;
26 class ShmemBuffer {
27 public:
28 ShmemBuffer() : mInitialized(false) {}
29 explicit ShmemBuffer(mozilla::ipc::Shmem aShmem) {
30 mInitialized = true;
31 mShmem = aShmem;
34 ShmemBuffer(ShmemBuffer&& rhs) {
35 mInitialized = rhs.mInitialized;
36 mShmem = std::move(rhs.mShmem);
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;
46 // No copies allowed
47 ShmemBuffer(const ShmemBuffer&) = delete;
48 ShmemBuffer& operator=(const ShmemBuffer&) = delete;
50 bool Valid() { return mInitialized; }
52 uint8_t* GetBytes() { return mShmem.get<uint8_t>(); }
54 mozilla::ipc::Shmem& Get() { return mShmem; }
56 private:
57 friend class ShmemPool;
59 bool mInitialized;
60 mozilla::ipc::Shmem mShmem;
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);
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;
87 enum class AllocationPolicy { Default, Unsafe };
89 template <class T>
90 ShmemBuffer Get(T* aInstance, size_t aSize,
91 AllocationPolicy aPolicy = AllocationPolicy::Default) {
92 MutexAutoLock lock(mMutex);
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"));
105 // This isn't initialized, so will be understood as an error.
106 return ShmemBuffer();
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++;
116 ShmemBuffer& res = mShmemPool[mPoolFree - 1];
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();
124 res.mInitialized = true;
127 MOZ_DIAGNOSTIC_ASSERT(res.mShmem.IsWritable(),
128 "Shmem in Pool is not writable?");
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;
145 MOZ_ASSERT(res.mShmem.IsWritable(),
146 "Shmem in Pool is not writable post resize?");
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));
156 #endif
157 return std::move(res);
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, ipc::SharedMemory::TYPE_BASIC,
166 &aRes.mShmem)) ||
167 (aPolicy == AllocationPolicy::Unsafe &&
168 aInstance->AllocUnsafeShmem(aSize, ipc::SharedMemory::TYPE_BASIC,
169 &aRes.mShmem));
171 const PoolType mPoolType;
172 Mutex mMutex MOZ_UNANNOTATED;
173 size_t mPoolFree;
174 bool mErrorLogged;
175 #ifdef DEBUG
176 size_t mMaxPoolUse;
177 #endif
178 nsTArray<ShmemBuffer> mShmemPool;
181 } // namespace mozilla
183 #endif // mozilla_ShmemPool_h