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 "circle_buffer.h"
17#include "base.h"
18#ifdef CONFIG_USE_JEMALLOC_DFX_INIF
19#include <malloc.h>
20#endif
21
22namespace Hdc {
23CircleBuffer::CircleBuffer()
24{
25    run_ = false;
26    TimerStart();
27#ifdef CONFIG_USE_JEMALLOC_DFX_INIF
28    mallopt(M_DELAYED_FREE, M_DELAYED_FREE_DISABLE);
29    mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_DISABLE);
30#endif
31}
32
33CircleBuffer::~CircleBuffer()
34{
35    TimerStop();
36    for (auto iter = buffers_.begin(); iter != buffers_.end();) {
37        Data *data = iter->second;
38        delete[] data->buf;
39        delete data;
40        iter = buffers_.erase(iter);
41    }
42}
43
44uint8_t *CircleBuffer::Malloc()
45{
46    const size_t bufSize = static_cast<size_t>(Base::GetUsbffsBulkSize());
47    uint8_t *buf = nullptr;
48    std::unique_lock<std::mutex> lock(mutex_);
49    for (auto iter = buffers_.begin(); iter != buffers_.end(); ++iter) {
50        Data *data = iter->second;
51        if (data->used == false) {
52            data->used = true;
53            data->begin = std::chrono::steady_clock::now();
54            buf = data->buf;
55            break;
56        }
57    }
58    if (buf == nullptr) {
59        Data *data = new(std::nothrow) Data();
60        if (data == nullptr) {
61            return nullptr;
62        }
63        data->used = true;
64        data->begin = std::chrono::steady_clock::now();
65        data->buf = new(std::nothrow) uint8_t[bufSize];
66        if (data->buf == nullptr) {
67            delete data;
68            return nullptr;
69        }
70        uint64_t key = reinterpret_cast<uint64_t>(data->buf);
71        buffers_[key] = data;
72        buf = data->buf;
73    }
74    (void)memset_s(buf, bufSize, 0, bufSize);
75    return buf;
76}
77
78void CircleBuffer::Free(const uint8_t *buf)
79{
80    std::unique_lock<std::mutex> lock(mutex_);
81    uint64_t key = reinterpret_cast<uint64_t>(buf);
82    Data *data = buffers_[key];
83    if (data != nullptr) {
84        data->used = false;
85        data->begin = std::chrono::steady_clock::now();
86    } else {
87        WRITE_LOG(LOG_FATAL, "Free data is nullptr.");
88    }
89}
90
91void CircleBuffer::FreeMemory()
92{
93    std::unique_lock<std::mutex> lock(mutex_);
94    constexpr int64_t decreaseTime = 5; // 5s
95    auto end = std::chrono::steady_clock::now();
96    for (auto iter = buffers_.begin(); iter != buffers_.end();) {
97        bool remove = false;
98        Data *data = iter->second;
99        if (data->used == false) {
100            auto begin = data->begin;
101            auto duration = std::chrono::duration_cast<std::chrono::seconds>(end - begin);
102            if (duration.count() > decreaseTime) {
103                remove = true;
104            }
105        }
106        if (remove) {
107            delete[] data->buf;
108            delete data;
109            iter = buffers_.erase(iter);
110        } else {
111            ++iter;
112        }
113    }
114}
115
116void CircleBuffer::Timer(void *object)
117{
118#ifdef CONFIG_USE_JEMALLOC_DFX_INIF
119    mallopt(M_DELAYED_FREE, M_DELAYED_FREE_DISABLE);
120    mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_DISABLE);
121#endif
122    CircleBuffer *cirbuf = reinterpret_cast<CircleBuffer *>(object);
123    while (cirbuf->run_) {
124        cirbuf->FreeMemory();
125        cirbuf->TimerSleep();
126    }
127}
128
129void CircleBuffer::TimerStart()
130{
131#ifndef HDC_HOST
132    if (!run_) {
133        run_ = true;
134        thread_ = std::thread([this]() { Timer(this); });
135    }
136#endif
137}
138
139void CircleBuffer::TimerStop()
140{
141#ifndef HDC_HOST
142    if (run_) {
143        run_ = false;
144        TimerNotify();
145        thread_.join();
146    }
147#endif
148}
149
150void CircleBuffer::TimerSleep()
151{
152    std::unique_lock<std::mutex> lock(timerMutex_);
153    timerCv_.wait_for(lock, std::chrono::seconds(1));
154}
155
156void CircleBuffer::TimerNotify()
157{
158    std::unique_lock<std::mutex> lock(timerMutex_);
159    timerCv_.notify_one();
160}
161}
162