/* * Copyright (C) 2021 Amlogic Corporation. * * 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 #include #include #include #include #include "Thread.h" namespace ATools { pthread_t getThreadId() { return (pthread_t)pthread_self(); } Thread::Thread() :mThread(pthread_t(-1)), mLock("Thread::mLock"), mStatus(0), mExitPending(false), mRunning(false), mPriority(0) { } Thread::~Thread() { } void Thread::setThreadPriority(int priority) { mPriority = priority; } void Thread::readyToRun() { } void Thread::readyToExit() { } int Thread::run(const char* name) { ATools::Mutex::Autolock _l(mLock); if (mRunning) { // thread already started return -1; } // reset status and exitPending to their default value, so we can // try again after an error happened (either below, or in readyToRun()) mStatus = 0; mExitPending = false; mThread = pthread_t(-1); memset(mThreadName, '\0', sizeof(mThreadName)); if (name) { if (strlen(name) <= sizeof(mThreadName)-1) { strcpy(mThreadName, name); } else { strncpy(mThreadName, name, sizeof(mThreadName)-1); } } else { strcpy(mThreadName, "unknown"); } //set running at this ,if request exit immediately when invoking run //this will let requestExitAndWait block until thread running mRunning = true; int res; res = _createThread(_threadLoop); if (res != 0) { mStatus = -1; // something happened! mRunning = false; mThread = pthread_t(-1); mCondition.broadcast(); return -1; } return 0; } int Thread::_createThread(pthread_entry_func entryFunction) { // pthread_attr_t attr; // pthread_attr_init(&attr); // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); errno = 0; int result = pthread_create(&mThread, NULL, (pthread_entry_func)entryFunction, (void *)this); // pthread_attr_destroy(&attr); if (result != 0) { return -1; } return 0; } void* Thread::_threadLoop(void* user) { Thread* const self = static_cast(user); struct sched_param schedParam; int maxPriority,miniPriority; self->mRunning = true; if (self->mPriority > 0) { maxPriority = sched_get_priority_max(SCHED_FIFO); miniPriority = sched_get_priority_min(SCHED_FIFO); schedParam.sched_priority = self->mPriority; if (self->mPriority > maxPriority) { schedParam.sched_priority = maxPriority; } else if (self->mPriority < miniPriority) { schedParam.sched_priority = miniPriority; } pthread_setschedparam(pthread_self(), SCHED_FIFO, &schedParam ); } pthread_setname_np(pthread_self(), self->mThreadName); self->readyToRun(); //maybe readyToRun will do something that take a long time, //so we need to check exit thread status if (self->mExitPending) { goto exit; } do { bool result = true; result = self->threadLoop(); if (result == false || self->mExitPending) { break; } } while(self->mRunning); exit: //must call readyToExit before set mRunning to false self->readyToExit(); ATools::Mutex::Autolock _l(self->mLock); self->mExitPending = true; // clear thread ID so that requestExitAndWait() does not exit if // called by a new thread using the same thread ID as this one. // self->mThread = pthread_t(-1); self->mRunning = false; // note that interested observers blocked in requestExitAndWait are // awoken by broadcast, but blocked on mLock until break exits scope self->mCondition.broadcast(); return 0; } void Thread::requestExit() { ATools::Mutex::Autolock _l(mLock); if (mThread >= 0) { int ret = pthread_detach(mThread); } mExitPending = true; } int Thread::requestExitAndWait() { // Tls::Mutex::Autolock _l(mLock); // if (mThread == getThreadId()) { // /*DEBUG( // "Thread (this=%p): don't call waitForExit() from this " // "Thread object's thread. It's a guaranteed deadlock!", // this);*/ // return ERROR_WOULD_BLOCK; // } mExitPending = true; if (mThread >= 0) { pthread_join(mThread,NULL); } // while (mRunning == true) { // mCondition.wait(mLock); // } // This next line is probably not needed any more, but is being left for // historical reference. Note that each interested party will clear flag. mExitPending = false; mThread = pthread_t(-1); return mStatus; } int Thread::join() { // Tls::Mutex::Autolock _l(mLock); // if (mThread == getThreadId()) { // /*DEBUG( // "Thread (this=%p): don't call join() from this " // "Thread object's thread. It's a guaranteed deadlock!", // this);*/ // return ERROR_WOULD_BLOCK; // } // while (mRunning == true) { // mCondition.wait(mLock); // } requestExitAndWait(); return mStatus; } bool Thread::isRunning() const { ATools::Mutex::Autolock _l(mLock); return mRunning; } bool Thread::isExitPending() const { ATools::Mutex::Autolock _l(mLock); return mExitPending; } }