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