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 22namespace OHOS { 23using ThreadFunc = int (*)(void*); 24using PThreadRoutine = void* (*) (void*); 25 26struct 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. 33 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 58bool 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 97Thread::Thread() 98 : thread_(INVALID_PTHREAD_T), status_(ThreadStatus::OK), exitPending_(false), running_(false) 99{ 100} 101 102Thread::~Thread() 103{ 104} 105 106ThreadStatus 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 136ThreadStatus Thread::NotifyExitSync() 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 // don't call NotifyExitSync() from this !; 141 return ThreadStatus::WOULD_BLOCK; 142 } 143 144 std::unique_lock<std::mutex> lk(lock_); 145 exitPending_ = true; 146 147 while (running_) { 148 cvThreadExited_.wait(lk); 149 } 150 151 exitPending_ = false; 152 153 return status_; 154} 155 156void Thread::NotifyExitAsync() 157{ 158 std::lock_guard<std::mutex> lk(lock_); 159 exitPending_ = true; 160} 161 162bool Thread::ReadyToWork() 163{ 164 return true; 165} 166 167bool Thread::IsExitPending() const 168{ 169 std::lock_guard<std::mutex> lk(lock_); 170 return exitPending_; 171} 172 173bool Thread::IsRunning() const 174{ 175 std::lock_guard<std::mutex> lk(lock_); 176 return running_; 177} 178 179int Thread::ThreadStart(void* args) 180{ 181 Thread* const self = static_cast<Thread*>(args); 182 bool first = true; 183 184 do { 185 bool result = false; 186 if (first) { 187 first = false; 188 if (self->ReadyToWork() && !self->IsExitPending()) { 189 result = self->Run(); 190 } 191 } else { 192 result = self->Run(); 193 } 194 195 { 196 std::unique_lock<std::mutex> lk(self->lock_); 197 if ((!result) || self->exitPending_) { 198 self->exitPending_ = true; 199 self->running_ = false; 200 self->thread_ = INVALID_PTHREAD_T; 201 self->cvThreadExited_.notify_all(); 202 break; 203 } 204 } 205 } while (true); 206 207 return 0; 208} 209 210} // namespace OHOS 211