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
21namespace {
22constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, LOG_DOMAIN_FOUNDATION, "AVSharedMemoryPool" };
23}
24
25namespace {
26    constexpr int32_t MAX_MEM_SIZE = 100 * 1024 * 1024;
27}
28
29namespace OHOS {
30namespace Media {
31AVSharedMemoryPool::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
36AVSharedMemoryPool::~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
42int32_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
68AVSharedMemory *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    memory->Init();
74    int32_t ret = memory->Init();
75    FALSE_RETURN_V_MSG_E(ret == 0, nullptr, "init avsharedmemorybase failed");
76
77    CANCEL_SCOPE_EXIT_GUARD(0);
78    return memory;
79}
80
81void AVSharedMemoryPool::ReleaseMemory(AVSharedMemory *memory)
82{
83    FALSE_LOG_MSG(memory != nullptr, "memory is nullptr");
84    std::unique_lock<std::mutex> lock(mutex_);
85
86    for (auto iter = busyList_.begin(); iter != busyList_.end(); ++iter) {
87        if (*iter != memory) {
88            continue;
89        }
90
91        busyList_.erase(iter);
92        idleList_.push_back(memory);
93        cond_.notify_all();
94        MEDIA_LOG_D("0x%{public}06" PRIXPTR " released back to pool %{public}s",
95                    FAKE_POINTER(memory), name_.c_str());
96
97        lock.unlock();
98        if (notifier_ != nullptr) {
99            notifier_();
100        }
101        return;
102    }
103
104    MEDIA_LOG_E("0x%{public}06" PRIXPTR " is no longer managed by this pool", FAKE_POINTER(memory));
105    delete memory;
106}
107
108bool AVSharedMemoryPool::DoAcquireMemory(int32_t size, AVSharedMemory **outMemory)
109{
110    MEDIA_LOG_D("busylist size " PUBLIC_LOG_ZU ", idlelist size " PUBLIC_LOG_ZU, busyList_.size(), idleList_.size());
111
112    AVSharedMemory *result = nullptr;
113    std::list<AVSharedMemory *>::iterator minSizeIdleMem = idleList_.end();
114    int32_t minIdleSize = std::numeric_limits<int32_t>::max();
115
116    for (auto iter = idleList_.begin(); iter != idleList_.end(); ++iter) {
117        if ((*iter)->GetSize() >= size) {
118            result = *iter;
119            idleList_.erase(iter);
120            break;
121        }
122
123        if ((*iter)->GetSize() < minIdleSize) {
124            minIdleSize = (*iter)->GetSize();
125            minSizeIdleMem = iter;
126        }
127    }
128
129    if (result == nullptr) {
130        auto totalCnt = busyList_.size() + idleList_.size();
131        if (totalCnt < option_.maxMemCnt) {
132            result = AllocMemory(size);
133            FALSE_RETURN_V_MSG_E(result != nullptr, false, "result is nullptr");
134            FALSE_RETURN_V(result != nullptr, false);
135        }
136
137        if (!option_.enableFixedSize && minSizeIdleMem != idleList_.end()) {
138            delete *minSizeIdleMem;
139            *minSizeIdleMem = nullptr;
140            idleList_.erase(minSizeIdleMem);
141            result = AllocMemory(size);
142            FALSE_RETURN_V_MSG_E(result != nullptr, false, "result is nullptr");
143            FALSE_RETURN_V(result != nullptr, false);
144        }
145    }
146
147    *outMemory = result;
148    return true;
149}
150
151bool AVSharedMemoryPool::CheckSize(int32_t size)
152{
153    if (size <= 0 && size != -1) {
154        return false;
155    }
156
157    if (!option_.enableFixedSize && size == -1) {
158        return false;
159    }
160
161    if (option_.enableFixedSize) {
162        if (size > option_.memSize) {
163            return false;
164        }
165
166        if (size <= 0 && size != -1) {
167            return false;
168        }
169    }
170
171    return true;
172}
173
174std::shared_ptr<AVSharedMemory> AVSharedMemoryPool::AcquireMemory(int32_t size, bool blocking)
175{
176    MEDIA_LOG_D("acquire memory for size: %{public}d from pool %{public}s, blocking: %{public}d",
177        size, name_.c_str(), blocking);
178
179    std::unique_lock<std::mutex> lock(mutex_);
180    FALSE_RETURN_V_MSG_E(CheckSize(size), nullptr, "invalid size: %{public}d", size);
181
182    if (option_.enableFixedSize) {
183        size = option_.memSize;
184    }
185
186    AVSharedMemory *memory = nullptr;
187    do {
188        if (!DoAcquireMemory(size, &memory) || memory != nullptr) {
189            break;
190        }
191
192        if (!blocking || forceNonBlocking_) {
193            break;
194        }
195
196        cond_.wait(lock);
197    } while (inited_ && !forceNonBlocking_);
198
199    FALSE_RETURN_V_MSG_E(memory != nullptr, nullptr, "acquire memory failed for size: %{public}d", size);
200    busyList_.push_back(memory);
201
202    auto result = std::shared_ptr<AVSharedMemory>(memory, [weakPool = weak_from_this()](AVSharedMemory *memory) {
203        std::shared_ptr<AVSharedMemoryPool> pool = weakPool.lock();
204        if (pool != nullptr) {
205            pool->ReleaseMemory(memory);
206        } else {
207            MEDIA_LOG_I("release memory 0x%{public}06" PRIXPTR ", but the pool is destroyed", FAKE_POINTER(memory));
208            delete memory;
209        }
210    });
211
212    MEDIA_LOG_D("0x%{public}06" PRIXPTR " acquired from pool", FAKE_POINTER(memory));
213    return result;
214}
215
216void AVSharedMemoryPool::SetNonBlocking(bool enable)
217{
218    std::unique_lock<std::mutex> lock(mutex_);
219    MEDIA_LOG_D("SetNonBlocking: %{public}d", enable);
220    forceNonBlocking_ = enable;
221    if (forceNonBlocking_) {
222        cond_.notify_all();
223    }
224}
225
226void AVSharedMemoryPool::Reset()
227{
228    MEDIA_LOG_D("Reset");
229
230    std::unique_lock<std::mutex> lock(mutex_);
231    for (auto &memory : idleList_) {
232        delete memory;
233        memory = nullptr;
234    }
235    idleList_.clear();
236    inited_ = false;
237    forceNonBlocking_ = false;
238    notifier_ = nullptr;
239    cond_.notify_all();
240    // for busylist, the memory will be released when the refcount of shared_ptr is zero.
241}
242} // namespace Media
243} // namespace OHOS