1 /*
2 * Copyright (c) 2021 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 "thread_ex.h"
17 #include <sys/resource.h>
18 #include <sys/prctl.h>
19 #include "utils_log.h"
20
21 namespace OHOS {
22 using ThreadFunc = int (*)(void*);
23 using PThreadRoutine = void* (*) (void*);
24
25 struct ThreadParam {
26 ThreadFunc startRoutine;
27 void* args;
28 int priority;
29 std::string name;
30
31 // prctl only support set the name of the calling process.
ProxyOHOS::ThreadParam32 static int Proxy(const ThreadParam* t)
33 {
34 if (t == nullptr) {
35 UTILS_LOGD("invalid param.");
36 return -1;
37 }
38 ThreadFunc f = t->startRoutine;
39 void* userData = t->args;
40 int prio = t->priority;
41 std::string threadName = t->name;
42
43 delete t;
44
45 // set thread priority
46 (void)setpriority(PRIO_PROCESS, 0, prio);
47
48 // set thread name
49 if (!threadName.empty()) {
50 prctl(PR_SET_NAME, threadName.substr(0, MAX_THREAD_NAME_LEN).c_str(), 0, 0, 0);
51 }
52
53 return f(userData);
54 }
55 };
56
CreatePThread(ThreadParam & para,size_t stackSize,pthread_t * threadId)57 bool CreatePThread(ThreadParam& para, size_t stackSize, pthread_t *threadId)
58
59 {
60 pthread_attr_t attr;
61 pthread_attr_init(&attr);
62
63 // create thread as "detached", so it cleans up after itself.
64 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
65
66 auto t = new ThreadParam; // t would be delete in ThreadParam::Proxy
67 t->startRoutine = para.startRoutine;
68 t->args = para.args;
69 t->priority = para.priority;
70 t->name = para.name;
71
72 para.startRoutine = reinterpret_cast<ThreadFunc>(&ThreadParam::Proxy);
73 para.args = t;
74
75 if (stackSize) {
76 pthread_attr_setstacksize(&attr, stackSize);
77 }
78
79 errno = 0;
80 pthread_t thread;
81 int result = pthread_create(&thread, &attr, reinterpret_cast<PThreadRoutine>(para.startRoutine), para.args);
82 pthread_attr_destroy(&attr);
83 para.args = nullptr;
84
85 if (result != 0) {
86 return false;
87 }
88
89 if (threadId != nullptr) {
90 *threadId = thread;
91 }
92
93 return true;
94 }
95
Thread()96 Thread::Thread()
97 : thread_(INVALID_PTHREAD_T), status_(ThreadStatus::OK), exitPending_(false), running_(false)
98 {
99 }
100
~Thread()101 Thread::~Thread()
102 {
103 }
104
Start(const std::string & name,int32_t priority,size_t stack)105 ThreadStatus Thread::Start(const std::string& name, int32_t priority, size_t stack)
106 {
107 std::lock_guard<std::mutex> lk(lock_);
108 if (running_) {
109 // already started
110 return ThreadStatus::INVALID_OPERATION;
111 }
112
113 status_ = ThreadStatus::OK;
114 exitPending_ = false;
115 thread_ = INVALID_PTHREAD_T;
116 running_ = true;
117
118 ThreadParam para;
119 para.startRoutine = ThreadStart;
120 para.args = this;
121 para.name = name;
122 para.priority = priority;
123
124 bool res = CreatePThread(para, stack, &thread_);
125 if (!res) {
126 status_ = ThreadStatus::UNKNOWN_ERROR; // something happened!
127 running_ = false;
128 thread_ = INVALID_PTHREAD_T;
129 return ThreadStatus::UNKNOWN_ERROR;
130 }
131
132 return ThreadStatus::OK;
133 }
134
NotifyExitSync()135 ThreadStatus Thread::NotifyExitSync()
136 {
137 // If the two thread IDs are equal, pthread_equal() returns a non-zero value; otherwise, it returns 0.
138 if (pthread_equal(thread_, pthread_self()) != 0) {
139 // don't call NotifyExitSync() from this !;
140 return ThreadStatus::WOULD_BLOCK;
141 }
142
143 std::unique_lock<std::mutex> lk(lock_);
144 exitPending_ = true;
145
146 while (running_) {
147 cvThreadExited_.wait(lk);
148 }
149
150 exitPending_ = false;
151
152 return status_;
153 }
154
NotifyExitAsync()155 void Thread::NotifyExitAsync()
156 {
157 std::lock_guard<std::mutex> lk(lock_);
158 exitPending_ = true;
159 }
160
ReadyToWork()161 bool Thread::ReadyToWork()
162 {
163 return true;
164 }
165
IsExitPending() const166 bool Thread::IsExitPending() const
167 {
168 std::lock_guard<std::mutex> lk(lock_);
169 return exitPending_;
170 }
171
IsRunning() const172 bool Thread::IsRunning() const
173 {
174 std::lock_guard<std::mutex> lk(lock_);
175 return running_;
176 }
177
ThreadStart(void * args)178 int Thread::ThreadStart(void* args)
179 {
180 Thread* const self = static_cast<Thread*>(args);
181 bool first = true;
182
183 do {
184 bool result = false;
185 if (first) {
186 first = false;
187 if (self->ReadyToWork() && !self->IsExitPending()) {
188 result = self->Run();
189 }
190 } else {
191 result = self->Run();
192 }
193
194 {
195 std::unique_lock<std::mutex> lk(self->lock_);
196 if ((!result) || self->exitPending_) {
197 self->exitPending_ = true;
198 self->running_ = false;
199 self->thread_ = INVALID_PTHREAD_T;
200 self->cvThreadExited_.notify_all();
201 break;
202 }
203 }
204 } while (true);
205
206 return 0;
207 }
208
209 } // namespace OHOS
210