1/*
2 * Copyright (C) 2024 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#include <map>
16#include "dhcp_thread.h"
17#include "dhcp_logger.h"
18#if DHCP_FFRT_ENABLE
19#include "ffrt_inner.h"
20#else
21#include <atomic>
22#include <chrono>
23#include <condition_variable>
24#include <deque>
25#include <memory>
26#include <mutex>
27#include <thread>
28#ifndef OHOS_ARCH_LITE
29#include "common_timer_errors.h"
30#include "timer.h"
31#endif
32#endif
33namespace OHOS {
34namespace DHCP {
35DEFINE_DHCPLOG_DHCP_LABEL("DhcpThread");
36#if DHCP_FFRT_ENABLE
37class DhcpThread::DhcpThreadImpl {
38public:
39    DhcpThreadImpl(const std::string &threadName)
40    {
41        std::lock_guard<ffrt::mutex> lock(eventQurueMutex);
42        if (eventQueue != nullptr) {
43            DHCP_LOGI("DhcpThreadImpl already init.");
44            return;
45        }
46        eventQueue = std::make_shared<ffrt::queue>(threadName.c_str());
47        DHCP_LOGI("DhcpThreadImpl: Create a new eventQueue, threadName:%{public}s", threadName.c_str());
48    }
49    ~DhcpThreadImpl()
50    {
51        std::lock_guard<ffrt::mutex> lock(eventQurueMutex);
52        DHCP_LOGI("DhcpThread: ~DhcpThread");
53        if (eventQueue) {
54            eventQueue = nullptr;
55        }
56        for (auto iter = taskMap_.begin(); iter != taskMap_.end();) {
57            iter->second = nullptr;
58            iter = taskMap_.erase(iter);
59        }
60    }
61    bool PostSyncTask(Callback &callback)
62    {
63        std::lock_guard<ffrt::mutex> lock(eventQurueMutex);
64        if (eventQueue == nullptr) {
65            DHCP_LOGE("PostSyncTask: eventQueue is nullptr!");
66            return false;
67        }
68        DHCP_LOGD("PostSyncTask Enter");
69        ffrt::task_handle handle = eventQueue->submit_h(callback);
70        if (handle == nullptr) {
71            return false;
72        }
73        eventQueue->wait(handle);
74        return true;
75    }
76    bool PostAsyncTask(Callback &callback, int64_t delayTime = 0)
77    {
78        std::lock_guard<ffrt::mutex> lock(eventQurueMutex);
79        if (eventQueue == nullptr) {
80            DHCP_LOGE("PostAsyncTask: eventQueue is nullptr!");
81            return false;
82        }
83        int64_t delayTimeUs = delayTime * 1000;
84        DHCP_LOGD("PostAsyncTask Enter");
85        ffrt::task_handle handle = eventQueue->submit_h(callback, ffrt::task_attr().delay(delayTimeUs));
86        return handle != nullptr;
87    }
88    bool PostAsyncTask(Callback &callback, const std::string &name, int64_t delayTime = 0)
89    {
90        std::lock_guard<ffrt::mutex> lock(eventQurueMutex);
91        if (eventQueue == nullptr) {
92            DHCP_LOGE("PostAsyncTask: eventQueue is nullptr!");
93            return false;
94        }
95        int64_t delayTimeUs = delayTime * 1000;
96        DHCP_LOGD("PostAsyncTask Enter %{public}s", name.c_str());
97        ffrt::task_handle handle = eventQueue->submit_h(
98            callback, ffrt::task_attr().name(name.c_str()).delay(delayTimeUs));
99        if (handle == nullptr) {
100            return false;
101        }
102        taskMap_[name] = std::move(handle);
103        return true;
104    }
105    void RemoveAsyncTask(const std::string &name)
106    {
107        std::lock_guard<ffrt::mutex> lock(eventQurueMutex);
108        DHCP_LOGD("RemoveAsyncTask Enter %{public}s", name.c_str());
109        auto item = taskMap_.find(name);
110        if (item == taskMap_.end()) {
111            DHCP_LOGD("task not found");
112            return;
113        }
114        if (item->second != nullptr && eventQueue != nullptr) {
115            int32_t ret = eventQueue->cancel(item->second);
116            if (ret != 0) {
117                DHCP_LOGE("RemoveAsyncTask failed, error code : %{public}d", ret);
118            }
119        }
120        taskMap_.erase(name);
121    }
122private:
123    std::shared_ptr<ffrt::queue> eventQueue = nullptr;
124    mutable ffrt::mutex eventQurueMutex;
125    std::map<std::string, ffrt::task_handle> taskMap_;
126};
127#else
128class DhcpThread::DhcpThreadImpl {
129public:
130    DhcpThreadImpl(const std::string &threadName)
131    {
132        mRunFlag = true;
133        mWorkerThread = std::thread(DhcpThreadImpl::Run, std::ref(*this));
134        pthread_setname_np(mWorkerThread.native_handle(), threadName.c_str());
135    }
136    ~DhcpThreadImpl()
137    {
138        mRunFlag = false;
139        mCondition.notify_one();
140        if (mWorkerThread.joinable()) {
141            mWorkerThread.join();
142        }
143    }
144    bool PostSyncTask(Callback &callback)
145    {
146        DHCP_LOGE("DhcpThreadImpl PostSyncTask Unsupported in lite.");
147        return false;
148    }
149    bool PostAsyncTask(Callback &callback, int64_t delayTime = 0)
150    {
151        if (delayTime > 0) {
152            DHCP_LOGE("DhcpThreadImpl PostAsyncTask with delayTime Unsupported in lite.");
153            return false;
154        }
155        DHCP_LOGD("PostAsyncTask Enter");
156        {
157            std::unique_lock<std::mutex> lock(mMutex);
158            mEventQue.push_back(callback);
159        }
160        mCondition.notify_one();
161        return true;
162    }
163    bool PostAsyncTask(Callback &callback, const std::string &name, int64_t delayTime = 0)
164    {
165        DHCP_LOGE("DhcpThreadImpl PostAsyncTask with name Unsupported in lite.");
166        return false;
167    }
168    void RemoveAsyncTask(const std::string &name)
169    {
170        DHCP_LOGE("DhcpThreadImpl RemoveAsyncTask Unsupported in lite.");
171    }
172private:
173    static  void Run(DhcpThreadImpl &instance)
174    {
175        while (instance.mRunFlag) {
176            std::unique_lock<std::mutex> lock(instance.mMutex);
177            while (instance.mEventQue.empty() && instance.mRunFlag) {
178                instance.mCondition.wait(lock);
179            }
180            if (!instance.mRunFlag) {
181                break;
182            }
183            Callback msg = instance.mEventQue.front();
184            instance.mEventQue.pop_front();
185            lock.unlock();
186            msg();
187        }
188        return;
189    }
190    std::thread mWorkerThread;
191    std::atomic<bool> mRunFlag;
192    std::mutex mMutex;
193    std::condition_variable mCondition;
194    std::deque<Callback> mEventQue;
195};
196#endif
197
198
199DhcpThread::DhcpThread(const std::string &threadName)
200    :ptr_(new DhcpThreadImpl(threadName))
201{}
202
203DhcpThread::~DhcpThread()
204{
205    ptr_.reset();
206}
207
208bool DhcpThread::PostSyncTask(const Callback &callback)
209{
210    if (ptr_ == nullptr) {
211        DHCP_LOGE("PostSyncTask: ptr_ is nullptr!");
212        return false;
213    }
214    return ptr_->PostSyncTask(const_cast<Callback &>(callback));
215}
216
217bool DhcpThread::PostAsyncTask(const Callback &callback, int64_t delayTime)
218{
219    if (ptr_ == nullptr) {
220        DHCP_LOGE("PostAsyncTask: ptr_ is nullptr!");
221        return false;
222    }
223    return ptr_->PostAsyncTask(const_cast<Callback &>(callback), delayTime);
224}
225
226bool DhcpThread::PostAsyncTask(const Callback &callback, const std::string &name, int64_t delayTime)
227{
228    if (ptr_ == nullptr) {
229        DHCP_LOGE("PostAsyncTask: ptr_ is nullptr!");
230        return false;
231    }
232    return ptr_->PostAsyncTask(const_cast<Callback &>(callback), name, delayTime);
233}
234void DhcpThread::RemoveAsyncTask(const std::string &name)
235{
236    if (ptr_ == nullptr) {
237        DHCP_LOGE("RemoveAsyncTask: ptr_ is nullptr!");
238        return;
239    }
240    ptr_->RemoveAsyncTask(name);
241}
242
243#ifndef OHOS_ARCH_LITE
244DhcpTimer *DhcpTimer::GetInstance()
245{
246    static DhcpTimer instance;
247    return &instance;
248}
249#ifdef DHCP_FFRT_ENABLE
250DhcpTimer::DhcpTimer() : timer_(std::make_unique<DhcpThread>("DhcpTimer"))
251{
252    timerIdInit = 0;
253}
254
255DhcpTimer::~DhcpTimer()
256{
257    if (timer_) {
258        timer_.reset();
259    }
260}
261
262EnumErrCode DhcpTimer::Register(const TimerCallback &callback, uint32_t &outTimerId, uint32_t interval, bool once)
263{
264    if (timer_ == nullptr) {
265        DHCP_LOGE("timer_ is nullptr");
266        return DHCP_OPT_FAILED;
267    }
268    timerIdInit++;
269    bool ret = timer_->PostAsyncTask(callback, std::to_string(timerIdInit), interval);
270    if (!ret) {
271        DHCP_LOGE("Register timer failed");
272        timerIdInit--;
273        return DHCP_OPT_FAILED;
274    }
275
276    outTimerId = timerIdInit;
277    return DHCP_OPT_SUCCESS;
278}
279
280void DhcpTimer::UnRegister(uint32_t timerId)
281{
282    if (timerId == 0) {
283        DHCP_LOGE("timerId is 0, no register timer");
284        return;
285    }
286
287    if (timer_ == nullptr) {
288        DHCP_LOGE("timer_ is nullptr");
289        return;
290    }
291
292    timer_->RemoveAsyncTask(std::to_string(timerId));
293    return;
294}
295#else
296DhcpTimer::DhcpTimer() : timer_(std::make_unique<Utils::Timer>("DhcpTimer"))
297{
298    timer_->Setup();
299}
300
301DhcpTimer::~DhcpTimer()
302{
303    if (timer_) {
304        timer_->Shutdown(true);
305    }
306}
307
308EnumErrCode DhcpTimer::Register(const TimerCallback &callback, uint32_t &outTimerId, uint32_t interval, bool once)
309{
310    if (timer_ == nullptr) {
311        DHCP_LOGE("timer_ is nullptr");
312        return DHCP_OPT_FAILED;
313    }
314
315    uint32_t ret = timer_->Register(callback, interval, once);
316    if (ret == Utils::TIMER_ERR_DEAL_FAILED) {
317        DHCP_LOGE("Register timer failed");
318        return DHCP_OPT_FAILED;
319    }
320
321    outTimerId = ret;
322    return DHCP_OPT_SUCCESS;
323}
324
325void DhcpTimer::UnRegister(uint32_t timerId)
326{
327    if (timerId == 0) {
328        DHCP_LOGE("timerId is 0, no register timer");
329        return;
330    }
331
332    if (timer_ == nullptr) {
333        DHCP_LOGE("timer_ is nullptr");
334        return;
335    }
336
337    timer_->Unregister(timerId);
338    return;
339}
340#endif
341#endif
342}
343}