1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "buffer/avsharedmemorybase.h"
17 #include "common/avsharedmemorypool.h"
18 #include "common/log.h"
19 #include "scope_guard.h"
20
21 namespace {
22 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_FOUNDATION, "AVSharedMemoryPool" };
23 }
24
25 namespace {
26 constexpr int32_t MAX_MEM_SIZE = 100 * 1024 * 1024;
27 }
28
29 namespace OHOS {
30 namespace Media {
AVSharedMemoryPool(const std::string & name)31 AVSharedMemoryPool::AVSharedMemoryPool(const std::string &name) : name_(name)
32 {
33 MEDIA_LOG_D("enter ctor, 0x%{public}06" PRIXPTR ", name: %{public}s", FAKE_POINTER(this), name_.c_str());
34 }
35
~AVSharedMemoryPool()36 AVSharedMemoryPool::~AVSharedMemoryPool()
37 {
38 MEDIA_LOG_D("enter dtor, 0x%{public}06" PRIXPTR ", name: %{public}s", FAKE_POINTER(this), name_.c_str());
39 Reset();
40 }
41
Init(const InitializeOption & option)42 int32_t AVSharedMemoryPool::Init(const InitializeOption &option)
43 {
44 std::unique_lock<std::mutex> lock(mutex_);
45
46 FALSE_RETURN_V_MSG_E(!inited_, -1, "memory pool has inited.");
47 FALSE_RETURN_V_MSG_E(option.memSize < MAX_MEM_SIZE, -1, "memSize is larger than MAX_MEM_SIZE.");
48 FALSE_RETURN_V_MSG_E(option.maxMemCnt != 0, -1, "maxMemCnt is equal to zero.");
49
50 option_ = option;
51 option_.preAllocMemCnt = std::min(option.preAllocMemCnt, option.maxMemCnt);
52
53 MEDIA_LOG_I("name: %{public}s, init option: preAllocMemCnt = %{public}u, memSize = %{public}d, "
54 "maxMemCnt = %{public}u, enableFixedSize = %{public}d",
55 name_.c_str(), option_.preAllocMemCnt, option_.memSize, option_.maxMemCnt,
56 option_.enableFixedSize);
57 for (uint32_t i = 0; i < option_.preAllocMemCnt; ++i) {
58 auto memory = AllocMemory(option_.memSize);
59 FALSE_RETURN_V_MSG_E(memory != nullptr, -1, "failed to AllocMemory");
60 idleList_.push_back(memory);
61 }
62
63 inited_ = true;
64 notifier_ = option.notifier;
65 return 0;
66 }
67
AllocMemory(int32_t size)68 AVSharedMemory *AVSharedMemoryPool::AllocMemory(int32_t size)
69 {
70 AVSharedMemoryBase *memory = new (std::nothrow) AVSharedMemoryBase(size, option_.flags, name_);
71 FALSE_RETURN_V_MSG_E(memory != nullptr, nullptr, "create object failed");
72 ON_SCOPE_EXIT(0) { delete memory; };
73 int32_t ret = memory->Init();
74 FALSE_RETURN_V_MSG_E(ret == 0, nullptr, "init avsharedmemorybase failed");
75
76 CANCEL_SCOPE_EXIT_GUARD(0);
77 return memory;
78 }
79
ReleaseMemory(AVSharedMemory * memory)80 void AVSharedMemoryPool::ReleaseMemory(AVSharedMemory *memory)
81 {
82 FALSE_LOG_MSG(memory != nullptr, "memory is nullptr");
83 std::unique_lock<std::mutex> lock(mutex_);
84
85 for (auto iter = busyList_.begin(); iter != busyList_.end(); ++iter) {
86 if (*iter != memory) {
87 continue;
88 }
89
90 busyList_.erase(iter);
91 idleList_.push_back(memory);
92 cond_.notify_all();
93 MEDIA_LOG_D("0x%{public}06" PRIXPTR " released back to pool %{public}s",
94 FAKE_POINTER(memory), name_.c_str());
95
96 lock.unlock();
97 if (notifier_ != nullptr) {
98 notifier_();
99 }
100 return;
101 }
102
103 MEDIA_LOG_E("0x%{public}06" PRIXPTR " is no longer managed by this pool", FAKE_POINTER(memory));
104 delete memory;
105 }
106
DoAcquireMemory(int32_t size,AVSharedMemory ** outMemory)107 bool AVSharedMemoryPool::DoAcquireMemory(int32_t size, AVSharedMemory **outMemory)
108 {
109 MEDIA_LOG_D("busylist size " PUBLIC_LOG_ZU ", idlelist size " PUBLIC_LOG_ZU, busyList_.size(), idleList_.size());
110
111 AVSharedMemory *result = nullptr;
112 std::list<AVSharedMemory *>::iterator minSizeIdleMem = idleList_.end();
113 int32_t minIdleSize = std::numeric_limits<int32_t>::max();
114
115 for (auto iter = idleList_.begin(); iter != idleList_.end(); ++iter) {
116 if ((*iter)->GetSize() >= size) {
117 result = *iter;
118 idleList_.erase(iter);
119 break;
120 }
121
122 if ((*iter)->GetSize() < minIdleSize) {
123 minIdleSize = (*iter)->GetSize();
124 minSizeIdleMem = iter;
125 }
126 }
127
128 if (result == nullptr) {
129 auto totalCnt = busyList_.size() + idleList_.size();
130 if (totalCnt < option_.maxMemCnt) {
131 result = AllocMemory(size);
132 FALSE_RETURN_V_MSG_E(result != nullptr, false, "result is nullptr");
133 FALSE_RETURN_V(result != nullptr, false);
134 } else if (!option_.enableFixedSize && minSizeIdleMem != idleList_.end()) {
135 delete *minSizeIdleMem;
136 *minSizeIdleMem = nullptr;
137 idleList_.erase(minSizeIdleMem);
138 result = AllocMemory(size);
139 FALSE_RETURN_V_MSG_E(result != nullptr, false, "result is nullptr");
140 FALSE_RETURN_V(result != nullptr, false);
141 }
142 }
143
144 *outMemory = result;
145 return true;
146 }
147
CheckSize(int32_t size)148 bool AVSharedMemoryPool::CheckSize(int32_t size)
149 {
150 if (size <= 0 && size != -1) {
151 return false;
152 }
153
154 if (!option_.enableFixedSize && size == -1) {
155 return false;
156 }
157
158 if (option_.enableFixedSize) {
159 if (size > option_.memSize) {
160 return false;
161 }
162
163 if (size <= 0 && size != -1) {
164 return false;
165 }
166 }
167
168 return true;
169 }
170
AcquireMemory(int32_t size,bool blocking)171 std::shared_ptr<AVSharedMemory> AVSharedMemoryPool::AcquireMemory(int32_t size, bool blocking)
172 {
173 MEDIA_LOG_D("acquire memory for size: %{public}d from pool %{public}s, blocking: %{public}d",
174 size, name_.c_str(), blocking);
175
176 std::unique_lock<std::mutex> lock(mutex_);
177 FALSE_RETURN_V_MSG_E(CheckSize(size), nullptr, "invalid size: %{public}d", size);
178
179 if (option_.enableFixedSize) {
180 size = option_.memSize;
181 }
182
183 AVSharedMemory *memory = nullptr;
184 do {
185 if (!DoAcquireMemory(size, &memory) || memory != nullptr) {
186 break;
187 }
188
189 if (!blocking || forceNonBlocking_) {
190 break;
191 }
192
193 cond_.wait(lock);
194 } while (inited_ && !forceNonBlocking_);
195
196 FALSE_RETURN_V_MSG_E(memory != nullptr, nullptr, "acquire memory failed for size: %{public}d", size);
197 busyList_.push_back(memory);
198
199 auto result = std::shared_ptr<AVSharedMemory>(memory, [weakPool = weak_from_this()](AVSharedMemory *memory) {
200 std::shared_ptr<AVSharedMemoryPool> pool = weakPool.lock();
201 if (pool != nullptr) {
202 pool->ReleaseMemory(memory);
203 } else {
204 MEDIA_LOG_I("release memory 0x%{public}06" PRIXPTR ", but the pool is destroyed", FAKE_POINTER(memory));
205 delete memory;
206 }
207 });
208
209 MEDIA_LOG_D("0x%{public}06" PRIXPTR " acquired from pool", FAKE_POINTER(memory));
210 return result;
211 }
212
SetNonBlocking(bool enable)213 void AVSharedMemoryPool::SetNonBlocking(bool enable)
214 {
215 std::unique_lock<std::mutex> lock(mutex_);
216 MEDIA_LOG_D("SetNonBlocking: %{public}d", enable);
217 forceNonBlocking_ = enable;
218 if (forceNonBlocking_) {
219 cond_.notify_all();
220 }
221 }
222
Reset()223 void AVSharedMemoryPool::Reset()
224 {
225 MEDIA_LOG_D("Reset");
226
227 std::unique_lock<std::mutex> lock(mutex_);
228 for (auto &memory : idleList_) {
229 delete memory;
230 memory = nullptr;
231 }
232 idleList_.clear();
233 inited_ = false;
234 forceNonBlocking_ = false;
235 notifier_ = nullptr;
236 cond_.notify_all();
237 // for busylist, the memory will be released when the refcount of shared_ptr is zero.
238 }
239 } // namespace Media
240 } // namespace OHOS