• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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