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 "task_thread.h"
17#include "avcodec_log.h"
18
19namespace {
20    constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "TaskThread"};
21    constexpr uint8_t LOGD_FREQUENCY = 100;
22}
23namespace OHOS {
24namespace MediaAVCodec {
25TaskThread::TaskThread(std::string_view name) : name_(name), runningState_(RunningState::STOPPED), loop_(nullptr)
26{
27    AVCODEC_LOGD("task %{public}s ctor called", name_.data());
28}
29
30TaskThread::TaskThread(std::string_view name, std::function<void()> handler) : TaskThread(name)
31{
32    handler_ = std::move(handler);
33    loop_ = std::make_unique<std::thread>(&TaskThread::Run, this);
34}
35
36TaskThread::~TaskThread()
37{
38    AVCODEC_LOGD("task %{public}s dtor called", name_.data());
39    runningState_ = RunningState::STOPPED;
40    syncCond_.notify_all();
41
42    if (loop_ != nullptr) {
43        if (loop_->joinable()) {
44            loop_->join();
45        }
46        loop_ = nullptr;
47    }
48}
49
50void TaskThread::Start()
51{
52    std::unique_lock lock(stateMutex_);
53    if (runningState_.load() == RunningState::STOPPING) {
54        syncCond_.wait(lock, [this] { return runningState_.load() == RunningState::STOPPED; });
55    }
56    if (runningState_.load() == RunningState::STOPPED) {
57        if (loop_ != nullptr) {
58            if (loop_->joinable()) {
59                loop_->join();
60            }
61            loop_ = nullptr;
62        }
63    }
64
65    runningState_ = RunningState::STARTED;
66
67    if (!loop_) { // thread not exist
68        loop_ = std::make_unique<std::thread>(&TaskThread::Run, this);
69    }
70    syncCond_.notify_all();
71    AVCODEC_LOGD("task %{public}s start called", name_.data());
72}
73
74void TaskThread::Stop()
75{
76    AVCODEC_LOGD("task %{public}s stop entered, current state: %{public}d", name_.data(), runningState_.load());
77    std::unique_lock lock(stateMutex_);
78    if (runningState_.load() != RunningState::STOPPED) {
79        runningState_ = RunningState::STOPPING;
80        syncCond_.notify_all();
81        syncCond_.wait(lock, [this] { return runningState_.load() == RunningState::STOPPED; });
82        if (loop_ != nullptr) {
83            if (loop_->joinable()) {
84                loop_->join();
85            }
86            loop_ = nullptr;
87        }
88    }
89    AVCODEC_LOGD("task %{public}s stop exited", name_.data());
90}
91
92void TaskThread::StopAsync()
93{
94    AVCODEC_LOGD("task %{public}s StopAsync called", name_.data());
95    std::unique_lock lock(stateMutex_);
96    if (runningState_.load() != RunningState::STOPPING && runningState_.load() != RunningState::STOPPED) {
97        runningState_ = RunningState::STOPPING;
98        syncCond_.notify_all();
99    }
100}
101
102void TaskThread::Pause()
103{
104    AVCODEC_LOGD("task %{public}s Pause called", name_.data());
105    std::unique_lock lock(stateMutex_);
106    switch (runningState_.load()) {
107        case RunningState::STARTED: {
108            runningState_ = RunningState::PAUSING;
109            syncCond_.wait(lock, [this] {
110                return runningState_.load() == RunningState::PAUSED || runningState_.load() == RunningState::STOPPED;
111            });
112            break;
113        }
114        case RunningState::STOPPING: {
115            syncCond_.wait(lock, [this] { return runningState_.load() == RunningState::STOPPED; });
116            break;
117        }
118        case RunningState::PAUSING: {
119            syncCond_.wait(lock, [this] { return runningState_.load() == RunningState::PAUSED; });
120            break;
121        }
122        default:
123            break;
124    }
125    AVCODEC_LOGD("task %{public}s Pause done.", name_.data());
126}
127
128void TaskThread::PauseAsync()
129{
130    AVCODEC_LOGD("task %{public}s PauseAsync called", name_.data());
131    std::unique_lock lock(stateMutex_);
132    if (runningState_.load() == RunningState::STARTED) {
133        runningState_ = RunningState::PAUSING;
134    }
135}
136
137void TaskThread::RegisterHandler(std::function<void()> handler)
138{
139    AVCODEC_LOGD("task %{public}s RegisterHandler called", name_.data());
140    handler_ = std::move(handler);
141}
142
143void TaskThread::doTask()
144{
145    AVCODEC_LOGD("task %{public}s not override DoTask...", name_.data());
146}
147
148void TaskThread::Run()
149{
150    // The max length for a thread name is 16.
151    auto ret = pthread_setname_np(pthread_self(), name_.data());
152    if (ret != 0) {
153        AVCODEC_LOGE("task %{public}s set name failed", name_.data());
154    }
155    for (;;) {
156        AVCODEC_LOGD_LIMIT(LOGD_FREQUENCY, "task %{public}s is running on state : %{public}d",
157            name_.data(), runningState_.load());
158        if (runningState_.load() == RunningState::STARTED) {
159            handler_();
160        }
161        std::unique_lock lock(stateMutex_);
162        if (runningState_.load() == RunningState::PAUSING || runningState_.load() == RunningState::PAUSED) {
163            runningState_ = RunningState::PAUSED;
164            syncCond_.notify_all();
165            constexpr int timeoutMs = 500;
166            syncCond_.wait_for(lock, std::chrono::milliseconds(timeoutMs),
167                               [this] { return runningState_.load() != RunningState::PAUSED; });
168        }
169        if (runningState_.load() == RunningState::STOPPING || runningState_.load() == RunningState::STOPPED) {
170            runningState_ = RunningState::STOPPED;
171            syncCond_.notify_all();
172            break;
173        }
174    }
175}
176} // namespace MediaAVCodec
177} // namespace OHOS