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"
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)
28 ShmemBuffer() : mInitialized(false) {}
29 explicit ShmemBuffer(mozilla::ipc::Shmem 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
);
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
; }
57 friend class ShmemPool
;
60 mozilla::ipc::Shmem mShmem
;
63 class ShmemPool final
{
65 enum class PoolType
{ StaticPool
, DynamicPool
};
66 explicit ShmemPool(size_t aPoolSize
,
67 PoolType aPoolType
= PoolType::StaticPool
);
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.
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
};
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
) {
97 // log "out of pool" once as error to avoid log spam
100 ("ShmemPool is empty, future occurrences "
101 "will be logged as warnings"));
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
));
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();
141 res
.mInitialized
= true;
145 MOZ_ASSERT(res
.mShmem
.IsWritable(),
146 "Shmem in Pool is not writable post resize?");
150 size_t poolUse
= mShmemPool
.Length() - mPoolFree
;
151 if (poolUse
> mMaxPoolUse
) {
152 mMaxPoolUse
= poolUse
;
154 ("Maximum ShmemPool use increased: %zu buffers", mMaxPoolUse
));
157 return std::move(res
);
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
,
167 (aPolicy
== AllocationPolicy::Unsafe
&&
168 aInstance
->AllocUnsafeShmem(aSize
, ipc::SharedMemory::TYPE_BASIC
,
171 const PoolType mPoolType
;
172 Mutex mMutex MOZ_UNANNOTATED
;
178 nsTArray
<ShmemBuffer
> mShmemPool
;
181 } // namespace mozilla
183 #endif // mozilla_ShmemPool_h