From 88054aa3cf17a795fe7ac7e87d4bb4d77e07e3bc Mon Sep 17 00:00:00 2001 From: Terraneo Federico <fede.tft@miosix.org> Date: Sun, 23 Apr 2023 11:38:12 +0200 Subject: [PATCH] Logical reordering of class Thread member functions --- miosix/kernel/kernel.cpp | 360 +++++++++++++++++++-------------------- miosix/kernel/kernel.h | 166 +++++++++--------- 2 files changed, 262 insertions(+), 264 deletions(-) diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index f7adb14d..a575212a 100755 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -359,7 +359,7 @@ Thread *Thread::create(void *(*startfunc)(void *), unsigned int stacksize, } Thread *Thread::create(void (*startfunc)(void *), unsigned int stacksize, - Priority priority, void *argv, unsigned short options) + Priority priority, void *argv, unsigned short options) { //Just call the other version with a cast. return Thread::create(reinterpret_cast<void *(*)(void*)>(startfunc), @@ -371,12 +371,6 @@ void Thread::yield() miosix_private::doYield(); } -bool Thread::testTerminate() -{ - //Just reading, no need for critical section - return const_cast<Thread*>(runningThread)->flags.isDeleting(); -} - void Thread::sleep(unsigned int ms) { nanoSleepUntil(getTime()+mul32x32to64(ms,1000000)); @@ -412,6 +406,46 @@ void Thread::nanoSleepUntil(long long absoluteTimeNs) Thread::yield(); } +void Thread::wait() +{ + //pausing the kernel is not enough because of IRQwait and IRQwakeup + { + FastInterruptDisableLock lock; + const_cast<Thread*>(runningThread)->flags.IRQsetWait(true); + } + Thread::yield(); + //Return here after wakeup +} + +void Thread::IRQwait() +{ + const_cast<Thread*>(runningThread)->flags.IRQsetWait(true); +} + +void Thread::wakeup() +{ + //pausing the kernel is not enough because of IRQwait and IRQwakeup + { + FastInterruptDisableLock lock; + this->flags.IRQsetWait(false); + } + #ifdef SCHED_TYPE_EDF + yield();//The other thread might have a closer deadline + #endif //SCHED_TYPE_EDF +} + +void Thread::PKwakeup() +{ + //pausing the kernel is not enough because of IRQwait and IRQwakeup + FastInterruptDisableLock lock; + this->flags.IRQsetWait(false); +} + +void Thread::IRQwakeup() +{ + this->flags.IRQsetWait(false); +} + Thread *Thread::getCurrentThread() { Thread *result=const_cast<Thread*>(runningThread); @@ -422,6 +456,18 @@ Thread *Thread::getCurrentThread() return allocateIdleThread(); } +Thread *Thread::IRQgetCurrentThread() +{ + //Implementation is the same as getCurrentThread, but to keep a consistent + //interface this method is duplicated + Thread *result=const_cast<Thread*>(runningThread); + if(result) return result; + //This function must always return a pointer to a valid thread. The first + //time this is called before the kernel is started, however, runningThread + //is nullptr, thus we allocate the idle thread and return a pointer to that. + return allocateIdleThread(); +} + bool Thread::exists(Thread *p) { if(p==nullptr) return false; @@ -429,11 +475,22 @@ bool Thread::exists(Thread *p) return Scheduler::PKexists(p); } +bool Thread::IRQexists(Thread* p) +{ + if(p==nullptr) return false; + return Scheduler::PKexists(p); +} + Priority Thread::getPriority() { return Scheduler::getPriority(this); } +Priority Thread::IRQgetPriority() +{ + return Scheduler::IRQgetPriority(this); +} + void Thread::setPriority(Priority pr) { if(pr.validate()==false) return; @@ -474,34 +531,10 @@ void Thread::terminate() this->flags.IRQsetDeleting(); } -void Thread::wait() -{ - //pausing the kernel is not enough because of IRQwait and IRQwakeup - { - FastInterruptDisableLock lock; - const_cast<Thread*>(runningThread)->flags.IRQsetWait(true); - } - Thread::yield(); - //Return here after wakeup -} - -void Thread::wakeup() -{ - //pausing the kernel is not enough because of IRQwait and IRQwakeup - { - FastInterruptDisableLock lock; - this->flags.IRQsetWait(false); - } - #ifdef SCHED_TYPE_EDF - yield();//The other thread might have a closer deadline - #endif //SCHED_TYPE_EDF -} - -void Thread::PKwakeup() +bool Thread::testTerminate() { - //pausing the kernel is not enough because of IRQwait and IRQwakeup - FastInterruptDisableLock lock; - this->flags.IRQsetWait(false); + //Just reading, no need for critical section + return const_cast<Thread*>(runningThread)->flags.isDeleting(); } void Thread::detach() @@ -572,41 +605,6 @@ bool Thread::join(void** result) return true; } -Thread *Thread::IRQgetCurrentThread() -{ - //Implementation is the same as getCurrentThread, but to keep a consistent - //interface this method is duplicated - Thread *result=const_cast<Thread*>(runningThread); - if(result) return result; - //This function must always return a pointer to a valid thread. The first - //time this is called before the kernel is started, however, runningThread - //is nullptr, thus we allocate the idle thread and return a pointer to that. - return allocateIdleThread(); -} - -Priority Thread::IRQgetPriority() -{ - //Implementation is the same as getPriority, but to keep a consistent - //interface this method is duplicated - return Scheduler::IRQgetPriority(this); -} - -void Thread::IRQwait() -{ - const_cast<Thread*>(runningThread)->flags.IRQsetWait(true); -} - -void Thread::IRQwakeup() -{ - this->flags.IRQsetWait(false); -} - -bool Thread::IRQexists(Thread* p) -{ - if(p==nullptr) return false; - return Scheduler::PKexists(p); -} - const unsigned int *Thread::getStackBottom() { return getCurrentThread()->watermark+(WATERMARK_LEN/sizeof(unsigned int)); @@ -617,45 +615,16 @@ int Thread::getStackSize() return getCurrentThread()->stacksize; } -Thread *Thread::doCreate(void*(*startfunc)(void*) , unsigned int stacksize, - void* argv, unsigned short options, bool defaultReent) +void Thread::IRQstackOverflowCheck() { - unsigned int fullStackSize=WATERMARK_LEN+CTXSAVE_ON_STACK+stacksize; - - //Align fullStackSize to the platform required stack alignment - fullStackSize+=CTXSAVE_STACK_ALIGNMENT-1; - fullStackSize/=CTXSAVE_STACK_ALIGNMENT; - fullStackSize*=CTXSAVE_STACK_ALIGNMENT; - - //Allocate memory for the thread, return if fail - unsigned int *base=static_cast<unsigned int*>(malloc(sizeof(Thread)+ - fullStackSize)); - if(base==nullptr) return nullptr; - - //At the top of thread memory allocate the Thread class with placement new - void *threadClass=base+(fullStackSize/sizeof(unsigned int)); - Thread *thread=new (threadClass) Thread(base,stacksize,defaultReent); - - if(thread->cReentrancyData==nullptr) + const unsigned int watermarkSize=WATERMARK_LEN/sizeof(unsigned int); + for(unsigned int i=0;i<watermarkSize;i++) { - thread->~Thread(); - free(base); //Delete ALL thread memory - return nullptr; + if(runningThread->watermark[i]!=WATERMARK_FILL) errorHandler(STACK_OVERFLOW); } - - //Fill watermark and stack - memset(base, WATERMARK_FILL, WATERMARK_LEN); - base+=WATERMARK_LEN/sizeof(unsigned int); - memset(base, STACK_FILL, fullStackSize-WATERMARK_LEN); - - //On some architectures some registers are saved on the stack, therefore - //initCtxsave *must* be called after filling the stack. - miosix_private::initCtxsave(thread->ctxsave,startfunc, - reinterpret_cast<unsigned int*>(thread),argv); - - if((options & JOINABLE)==0) thread->flags.IRQsetDetached(); - thread->flags.t = thread; - return thread; + if(runningThread->ctxsave[stackPtrOffsetInCtxsave] < + reinterpret_cast<unsigned int>(runningThread->watermark+watermarkSize)) + errorHandler(STACK_OVERFLOW); } #ifdef WITH_PROCESSES @@ -688,84 +657,6 @@ bool Thread::IRQreportFault(const miosix_private::FaultData& fault) return true; } -#endif //WITH_PROCESSES - -void Thread::IRQstackOverflowCheck() -{ - const unsigned int watermarkSize=WATERMARK_LEN/sizeof(unsigned int); - for(unsigned int i=0;i<watermarkSize;i++) - { - if(runningThread->watermark[i]!=WATERMARK_FILL) errorHandler(STACK_OVERFLOW); - } - if(runningThread->ctxsave[stackPtrOffsetInCtxsave] < - reinterpret_cast<unsigned int>(runningThread->watermark+watermarkSize)) - errorHandler(STACK_OVERFLOW); -} - -void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv) -{ - void *result=nullptr; - #ifdef __NO_EXCEPTIONS - result=threadfunc(argv); - #else //__NO_EXCEPTIONS - try { - result=threadfunc(argv); - } catch(std::exception& e) { - errorLog("***An exception propagated through a thread\n"); - errorLog("what():%s\n",e.what()); - } catch(...) { - errorLog("***An exception propagated through a thread\n"); - } - #endif //__NO_EXCEPTIONS - //Thread returned from its entry point, so delete it - - //Since the thread is running, it cannot be in the sleepingList, so no need - //to remove it from the list - { - FastInterruptDisableLock lock; - const_cast<Thread*>(runningThread)->flags.IRQsetDeleted(); - - if(const_cast<Thread*>(runningThread)->flags.isDetached()==false) - { - //If thread is joinable, handle join - if(runningThread->joinData.waitingForJoin!=nullptr) - { - //Wake thread - runningThread->joinData.waitingForJoin->flags.IRQsetJoinWait(false); - } - //Set result - runningThread->joinData.result=result; - } else { - //If thread is detached, memory can be deallocated immediately - existDeleted=true; - } - } - Thread::yield();//Since the thread is now deleted, yield immediately. - //Will never reach here - errorHandler(UNEXPECTED); -} - -Thread *Thread::allocateIdleThread() -{ - //NOTE: this function is only called once before the kernel is started, so - //there are no concurrency issues, not even with interrupts - - // Create the idle and main thread - auto *idle=Thread::doCreate(idleThread,STACK_IDLE,nullptr,Thread::DEFAULT,true); - if(idle==nullptr) errorHandler(OUT_OF_MEMORY); - - // runningThread must point to a valid thread, so we make it point to the the idle one - runningThread=idle; - return idle; -} - -struct _reent *Thread::getCReent() -{ - return getCurrentThread()->cReentrancyData; -} - -#ifdef WITH_PROCESSES - miosix_private::SyscallParameters Thread::switchToUserspace() { miosix_private::portableSwitchToUserspace(); @@ -848,6 +739,109 @@ Thread::~Thread() #endif //WITH_PROCESSES } +Thread *Thread::doCreate(void*(*startfunc)(void*) , unsigned int stacksize, + void* argv, unsigned short options, bool defaultReent) +{ + unsigned int fullStackSize=WATERMARK_LEN+CTXSAVE_ON_STACK+stacksize; + + //Align fullStackSize to the platform required stack alignment + fullStackSize+=CTXSAVE_STACK_ALIGNMENT-1; + fullStackSize/=CTXSAVE_STACK_ALIGNMENT; + fullStackSize*=CTXSAVE_STACK_ALIGNMENT; + + //Allocate memory for the thread, return if fail + unsigned int *base=static_cast<unsigned int*>(malloc(sizeof(Thread)+ + fullStackSize)); + if(base==nullptr) return nullptr; + + //At the top of thread memory allocate the Thread class with placement new + void *threadClass=base+(fullStackSize/sizeof(unsigned int)); + Thread *thread=new (threadClass) Thread(base,stacksize,defaultReent); + + if(thread->cReentrancyData==nullptr) + { + thread->~Thread(); + free(base); //Delete ALL thread memory + return nullptr; + } + + //Fill watermark and stack + memset(base, WATERMARK_FILL, WATERMARK_LEN); + base+=WATERMARK_LEN/sizeof(unsigned int); + memset(base, STACK_FILL, fullStackSize-WATERMARK_LEN); + + //On some architectures some registers are saved on the stack, therefore + //initCtxsave *must* be called after filling the stack. + miosix_private::initCtxsave(thread->ctxsave,startfunc, + reinterpret_cast<unsigned int*>(thread),argv); + + if((options & JOINABLE)==0) thread->flags.IRQsetDetached(); + thread->flags.t = thread; + return thread; +} + +void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv) +{ + void *result=nullptr; + #ifdef __NO_EXCEPTIONS + result=threadfunc(argv); + #else //__NO_EXCEPTIONS + try { + result=threadfunc(argv); + } catch(std::exception& e) { + errorLog("***An exception propagated through a thread\n"); + errorLog("what():%s\n",e.what()); + } catch(...) { + errorLog("***An exception propagated through a thread\n"); + } + #endif //__NO_EXCEPTIONS + //Thread returned from its entry point, so delete it + + //Since the thread is running, it cannot be in the sleepingList, so no need + //to remove it from the list + { + FastInterruptDisableLock lock; + const_cast<Thread*>(runningThread)->flags.IRQsetDeleted(); + + if(const_cast<Thread*>(runningThread)->flags.isDetached()==false) + { + //If thread is joinable, handle join + if(runningThread->joinData.waitingForJoin!=nullptr) + { + //Wake thread + runningThread->joinData.waitingForJoin->flags.IRQsetJoinWait(false); + } + //Set result + runningThread->joinData.result=result; + } else { + //If thread is detached, memory can be deallocated immediately + existDeleted=true; + } + } + Thread::yield();//Since the thread is now deleted, yield immediately. + //Will never reach here + errorHandler(UNEXPECTED); +} + +Thread *Thread::allocateIdleThread() +{ + //NOTE: this function is only called once before the kernel is started, so + //there are no concurrency issues, not even with interrupts + + // Create the idle and main thread + auto *idle=Thread::doCreate(idleThread,STACK_IDLE,nullptr,Thread::DEFAULT,true); + if(idle==nullptr) errorHandler(OUT_OF_MEMORY); + + // runningThread must point to a valid thread, so we make it point to the the idle one + runningThread=idle; + return idle; +} + +struct _reent *Thread::getCReent() +{ + return getCurrentThread()->cReentrancyData; +} + // // class ThreadFlags // diff --git a/miosix/kernel/kernel.h b/miosix/kernel/kernel.h index 72b62990..342811e0 100755 --- a/miosix/kernel/kernel.h +++ b/miosix/kernel/kernel.h @@ -497,18 +497,6 @@ public: */ static void yield(); - /** - * This method needs to be called periodically inside the thread's main - * loop. - * \return true if somebody outside the thread called terminate() on this - * thread. - * - * If it returns true the thread must free all resources and terminate by - * returning from its main function. - * <br>Can be called when the kernel is paused. - */ - static bool testTerminate(); - /** * Put the thread to sleep for a number of milliseconds. * <br>The actual precision depends on the underlying hardware timer. @@ -551,6 +539,50 @@ public: * CANNOT be called when the kernel is paused. */ static void nanoSleepUntil(long long absoluteTimeNs); + + /** + * This method stops the thread until another thread calls wakeup() on this + * thread.<br>Calls to wait are not cumulative. If wait() is called two + * times, only one call to wakeup() is needed to wake the thread. + * <br>CANNOT be called when the kernel is paused. + */ + static void wait(); + + /** + * Same as wait(), but is meant to be used only inside an IRQ or when + * interrupts are disabled.<br> + * Note: this method is meant to put the current thread in wait status in a + * piece of code where interrupts are disbled; it returns immediately, so + * the user is responsible for re-enabling interrupts and calling yield to + * effectively put the thread in wait status. + * + * \code + * disableInterrupts(); + * ... + * Thread::IRQwait();//Return immediately + * enableInterrupts(); + * Thread::yield();//After this, thread is in wait status + * \endcode + */ + static void IRQwait(); + + /** + * Wakeup a thread. + * <br>CANNOT be called when the kernel is paused. + */ + void wakeup(); + + /** + * Wakeup a thread. + * <br>Can be called when the kernel is paused. + */ + void PKwakeup(); + + /** + * Same as wakeup(), but is meant to be used only inside an IRQ or when + * interrupts are disabled. + */ + void IRQwakeup(); /** * Return a pointer to the Thread class of the current thread. @@ -561,6 +593,12 @@ public: */ static Thread *getCurrentThread(); + /** + * Same as get_current_thread(), but meant to be used insida an IRQ, when + * interrupts are disabled or when the kernel is paused. + */ + static Thread *IRQgetCurrentThread(); + /** * Check if a thread exists * \param p thread to check @@ -573,6 +611,12 @@ public: */ static bool exists(Thread *p); + /** + * Same as exists() but is meant to be called only inside an IRQ or when + * interrupts are disabled. + */ + static bool IRQexists(Thread *p); + /** * Returns the priority of a thread.<br> * To get the priority of the current thread use: @@ -586,6 +630,12 @@ public: */ Priority getPriority(); + /** + * Same as getPriority(), but meant to be used inside an IRQ, when + * interrupts are disabled or when the kernel is paused. + */ + Priority IRQgetPriority(); + /** * Set the priority of this thread.<br> * This member function changed from previous Miosix versions since it is @@ -610,24 +660,16 @@ public: void terminate(); /** - * This method stops the thread until another thread calls wakeup() on this - * thread.<br>Calls to wait are not cumulative. If wait() is called two - * times, only one call to wakeup() is needed to wake the thread. - * <br>CANNOT be called when the kernel is paused. - */ - static void wait(); - - /** - * Wakeup a thread. - * <br>CANNOT be called when the kernel is paused. - */ - void wakeup(); - - /** - * Wakeup a thread. + * This method needs to be called periodically inside the thread's main + * loop. + * \return true if somebody outside the thread called terminate() on this + * thread. + * + * If it returns true the thread must free all resources and terminate by + * returning from its main function. * <br>Can be called when the kernel is paused. */ - void PKwakeup(); + static bool testTerminate(); /** * Detach the thread if it was joinable, otherwise do nothing.<br> @@ -661,48 +703,6 @@ public: */ bool join(void** result=nullptr); - /** - * Same as get_current_thread(), but meant to be used insida an IRQ, when - * interrupts are disabled or when the kernel is paused. - */ - static Thread *IRQgetCurrentThread(); - - /** - * Same as getPriority(), but meant to be used inside an IRQ, when - * interrupts are disabled or when the kernel is paused. - */ - Priority IRQgetPriority(); - - /** - * Same as wait(), but is meant to be used only inside an IRQ or when - * interrupts are disabled.<br> - * Note: this method is meant to put the current thread in wait status in a - * piece of code where interrupts are disbled; it returns immediately, so - * the user is responsible for re-enabling interrupts and calling yield to - * effectively put the thread in wait status. - * - * \code - * disableInterrupts(); - * ... - * Thread::IRQwait();//Return immediately - * enableInterrupts(); - * Thread::yield();//After this, thread is in wait status - * \endcode - */ - static void IRQwait(); - - /** - * Same as wakeup(), but is meant to be used only inside an IRQ or when - * interrupts are disabled. - */ - void IRQwakeup(); - - /** - * Same as exists() but is meant to be called only inside an IRQ or when - * interrupts are disabled. - */ - static bool IRQexists(Thread *p); - /** * \internal * This method is only meant to implement functions to check the available @@ -717,6 +717,13 @@ public: * \return the size of the stack of the current thread. */ static int getStackSize(); + + /** + * \internal + * Used before every context switch to check if the stack of the thread + * being preempted has overflowed + */ + static void IRQstackOverflowCheck(); #ifdef WITH_PROCESSES @@ -744,18 +751,14 @@ public: #endif //WITH_PROCESSES - /** - * \internal - * Used before every context switch to check if the stack of the thread - * being preempted has overflowed - */ - static void IRQstackOverflowCheck(); + //Unwanted methods + Thread(const Thread& p) = delete; + Thread& operator = (const Thread& p) = delete; private: - //Unwanted methods - Thread(const Thread& p);///< No public copy constructor - Thread& operator = (const Thread& p);///< No publc operator = - + /** + * Curren thread status + */ class ThreadFlags { public: @@ -878,6 +881,7 @@ private: bool isInUserspace() const { return flags & USERSPACE; } Thread* t; + private: ///\internal Thread is in the wait status. A call to wakeup will change ///this @@ -968,7 +972,7 @@ private: * resources to create one. */ static Thread *doCreate(void *(*startfunc)(void *), unsigned int stacksize, - void *argv, unsigned short options, bool defaultReent); + void *argv, unsigned short options, bool defaultReent); /** * Thread launcher, all threads start from this member function, which calls -- GitLab