/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "thread_ex.h" #include <sys/resource.h> #include <sys/prctl.h> #include <iostream> #include "utils_log.h" namespace OHOS { using ThreadFunc = int (*)(void*); using PThreadRoutine = void* (*) (void*); struct ThreadParam { ThreadFunc startRoutine; void* args; int priority; std::string name; // prctl only support set the name of the calling process. static int proxy(const ThreadParam* t) { if (t == nullptr) { UTILS_LOGE("invalid param."); return -1; } ThreadFunc f = t->startRoutine; void* userData = t->args; int prio = t->priority; std::string threadName = t->name; delete t; // set thread priority (void)setpriority(PRIO_PROCESS, 0, prio); // set thread name if (!threadName.empty()) { prctl(PR_SET_NAME, threadName.substr(0, MAX_THREAD_NAME_LEN).c_str(), 0, 0, 0); } return f(userData); } }; bool CreatePThread(ThreadParam& para, size_t stackSize, pthread_t *threadId) { pthread_attr_t attr; pthread_attr_init(&attr); // create thread as "detached", so it cleans up after itself. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); auto t = new ThreadParam; // t would be delete in ThreadParam::proxy t->startRoutine = para.startRoutine; t->args = para.args; t->priority = para.priority; t->name = para.name; para.startRoutine = (ThreadFunc)&ThreadParam::proxy; para.args = t; if (stackSize) { pthread_attr_setstacksize(&attr, stackSize); } errno = 0; pthread_t thread; int result = pthread_create(&thread, &attr, (PThreadRoutine)para.startRoutine, para.args); pthread_attr_destroy(&attr); if (result != 0) { return false; } if (threadId != NULL) { *threadId = thread; } return true; } Thread::Thread() : thread_(INVALID_PTHREAD_T), status_(ThreadStatus::OK), exitPending_(false), running_(false) { } Thread::~Thread() { } ThreadStatus Thread::Start(const std::string& name, int32_t priority, size_t stack) { std::lock_guard<std::mutex> lk(lock_); if (running_) { // already started return ThreadStatus::INVALID_OPERATION; } status_ = ThreadStatus::OK; exitPending_ = false; thread_ = INVALID_PTHREAD_T; running_ = true; ThreadParam para; para.startRoutine = ThreadStart; para.args = this; para.name = name; para.priority = priority; bool res = CreatePThread(para, stack, &thread_); if (!res) { status_ = ThreadStatus::UNKNOWN_ERROR; // something happened! running_ = false; thread_ = INVALID_PTHREAD_T; return ThreadStatus::UNKNOWN_ERROR; } return ThreadStatus::OK; } ThreadStatus Thread::Join() { // If the two thread IDs are equal, pthread_equal() returns a non-zero value; otherwise, it returns 0. if (pthread_equal(thread_, pthread_self()) != 0) { return ThreadStatus::WOULD_BLOCK; } std::unique_lock<std::mutex> lk(lock_); while (running_) { cvThreadExited_.wait(lk); } return status_; } ThreadStatus Thread::NotifyExitSync() { // If the two thread IDs are equal, pthread_equal() returns a non-zero value; otherwise, it returns 0. if (pthread_equal(thread_, pthread_self()) != 0) { // don't call NotifyExitSync() from this !; return ThreadStatus::WOULD_BLOCK; } std::unique_lock<std::mutex> lk(lock_); exitPending_ = true; while (running_ == true) { cvThreadExited_.wait(lk); } exitPending_ = false; return status_; } void Thread::NotifyExitAsync() { std::lock_guard<std::mutex> lk(lock_); exitPending_ = true; } bool Thread::ReadyToWork() { return true; } bool Thread::IsExitPending() const { std::lock_guard<std::mutex> lk(lock_); return exitPending_; } bool Thread::IsRunning() const { std::lock_guard<std::mutex> lk(lock_); return running_; } int Thread::ThreadStart(void* args) { Thread* const self = static_cast<Thread*>(args); bool first = true; do { bool result = false; if (first) { first = false; if (self->ReadyToWork() && !self->IsExitPending()) { result = self->Run(); } } else { result = self->Run(); } { std::unique_lock<std::mutex> lk(self->lock_); if ((!result) || self->exitPending_) { self->exitPending_ = true; self->running_ = false; self->thread_ = INVALID_PTHREAD_T; self->cvThreadExited_.notify_all(); break; } } } while (true); return 0; } } // namespace OHOS