From fb89041ea2773564d67a724012a8a2874cc5af18 Mon Sep 17 00:00:00 2001 From: Daniele Cattaneo <daniele3.cattaneo@mail.polimi.it> Date: Tue, 11 Apr 2023 19:35:37 +0200 Subject: [PATCH] Simplify code and remove duplication in ConditionVariable and pthread_cond_*. Signed-off-by: Terraneo Federico <fede.tft@miosix.org> --- miosix/kernel/pthread.cpp | 122 ++++++-------------------------------- miosix/kernel/sync.cpp | 50 ++++++++++++++-- miosix/kernel/sync.h | 40 ++++++++++--- 3 files changed, 95 insertions(+), 117 deletions(-) diff --git a/miosix/kernel/pthread.cpp b/miosix/kernel/pthread.cpp index d8b7d1f4..e86836e2 100644 --- a/miosix/kernel/pthread.cpp +++ b/miosix/kernel/pthread.cpp @@ -291,103 +291,54 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) // Condition variable API // -static_assert(sizeof(IntrusiveList<CondData>)==sizeof(pthread_cond_t),"Invalid pthread_cond_t size"); +//The pthread_cond_t API is implemented simply as a wrapper around the native +//Miosix C++ ConditionVariable. Therefore the memory layout of pthread_cond_t +//and of ConditionVariable must be exactly the same. + +static_assert(sizeof(ConditionVariable)==sizeof(pthread_cond_t),"Invalid pthread_cond_t size"); int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) { //attr is currently not considered //NOTE: pthread_condattr_setclock is not supported, the only clock supported //for pthread_cond_timedwait is CLOCK_MONOTONIC - new (cond) IntrusiveList<CondData>; //Placement new as cond is a C type + new (cond) ConditionVariable; //Placement new as cond is a C type return 0; } int pthread_cond_destroy(pthread_cond_t *cond) { - auto *condList=reinterpret_cast<IntrusiveList<CondData>*>(cond); - if(!condList->empty()) return EBUSY; - condList->~IntrusiveList<CondData>(); //Call destructor manually + auto *impl=reinterpret_cast<ConditionVariable*>(cond); + if(!impl->condList.empty()) return EBUSY; + impl->~ConditionVariable(); //Call destructor manually return 0; } int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { - auto *condList=reinterpret_cast<IntrusiveList<CondData>*>(cond); - - FastInterruptDisableLock dLock; - Thread *t=Thread::IRQgetCurrentThread(); - CondData listItem(t); - condList->push_back(&listItem); //Putting this thread last on the list (lifo policy) - t->flags.IRQsetCondWait(true); - - unsigned int depth=IRQdoMutexUnlockAllDepthLevels(mutex); - { - FastInterruptEnableLock eLock(dLock); - Thread::yield(); //Here the wait becomes effective - } - IRQdoMutexLockToDepth(mutex,dLock,depth); + auto *impl=reinterpret_cast<ConditionVariable*>(cond); + impl->wait(mutex); return 0; } int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) { - return pthreadCondTimedWaitImpl(cond,mutex,timespec2ll(abstime)); + auto *impl=reinterpret_cast<ConditionVariable*>(cond); + TimedWaitResult res = impl->timedWait(mutex,timespec2ll(abstime)); + return res == TimedWaitResult::Timeout ? ETIMEDOUT : 0; } int pthread_cond_signal(pthread_cond_t *cond) { - auto *condList=reinterpret_cast<IntrusiveList<CondData>*>(cond); - - #ifdef SCHED_TYPE_EDF - bool hppw=false; - #endif //SCHED_TYPE_EDF - { - FastInterruptDisableLock dLock; - if(condList->empty()) return 0; - - Thread *t=condList->front()->thread; - condList->pop_front(); - t->flags.IRQsetCondWait(false); - t->flags.IRQsetSleep(false); //Needed due to timedwait - - #ifdef SCHED_TYPE_EDF - if(t->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) - hppw=true; - #endif //SCHED_TYPE_EDF - } - #ifdef SCHED_TYPE_EDF - //If the woken thread has higher priority, yield - if(hppw) Thread::yield(); - #endif //SCHED_TYPE_EDF + auto *impl=reinterpret_cast<ConditionVariable*>(cond); + impl->signal(); return 0; } int pthread_cond_broadcast(pthread_cond_t *cond) { - auto *condList=reinterpret_cast<IntrusiveList<CondData>*>(cond); - - #ifdef SCHED_TYPE_EDF - bool hppw=false; - #endif //SCHED_TYPE_EDF - { - FastInterruptDisableLock lock; - while(!condList->empty()) - { - Thread *t=condList->front()->thread; - condList->pop_front(); - t->flags.IRQsetCondWait(false); - t->flags.IRQsetSleep(false); //Needed due to timedwait - - #ifdef SCHED_TYPE_EDF - if(t->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) - hppw=true; - #endif //SCHED_TYPE_EDF - } - } - #ifdef SCHED_TYPE_EDF - //If at least one of the woken thread has higher, yield - if(hppw) Thread::yield(); - #endif //SCHED_TYPE_EDF + auto *impl=reinterpret_cast<ConditionVariable*>(cond); + impl->broadcast(); return 0; } @@ -441,40 +392,3 @@ int pthread_setcancelstate(int state, int *oldstate) { return 0; } //Stub } //extern "C" -namespace miosix { - -int pthreadCondTimedWaitImpl(pthread_cond_t *cond, pthread_mutex_t *mutex, long long absTime) -{ - auto *condList=reinterpret_cast<IntrusiveList<CondData>*>(cond); - - //Disallow absolute sleeps with negative or too low values, as the ns2tick() - //algorithm in TimeConversion can't handle negative values and may undeflow - //even with very low values due to a negative adjustOffsetNs. As an unlikely - //side effect, very shor sleeps done very early at boot will be extended. - absTime=std::max(absTime,100000LL); - FastInterruptDisableLock dLock; - Thread *t=Thread::IRQgetCurrentThread(); - CondData listItem(t); - condList->push_back(&listItem); //Putting this thread last on the list (lifo policy) - SleepData sleepData(t,absTime); - IRQaddToSleepingList(&sleepData); //Putting this thread on the sleeping list too - t->flags.IRQsetCondWait(true); - - unsigned int depth=IRQdoMutexUnlockAllDepthLevels(mutex); - { - FastInterruptEnableLock eLock(dLock); - Thread::yield(); //Here the wait becomes effective - } - //Ensure that the thread is removed from both list, as it can be woken by - //either a signal/broadcast (that removes it from condList) or by - //IRQwakeThreads (that removes it from sleeping list). - bool removed=condList->removeFast(&listItem); - IRQremoveFromSleepingList(&sleepData); - - IRQdoMutexLockToDepth(mutex,dLock,depth); - - //If the thread was still in the cond variable list, it was woken up by a timeout - return removed ? ETIMEDOUT : 0; -} - -} //namespace miosix diff --git a/miosix/kernel/sync.cpp b/miosix/kernel/sync.cpp index 456b3782..90fce52b 100644 --- a/miosix/kernel/sync.cpp +++ b/miosix/kernel/sync.cpp @@ -365,7 +365,7 @@ unsigned int Mutex::PKunlockAllDepthLevels(PauseKernelLock& dLock) // class ConditionVariable // -//Memory layout must be kept in sync with pthread_cond, see wait(FastMutex& m) +//Memory layout must be kept in sync with pthread_cond, see pthread.cpp static_assert(sizeof(ConditionVariable)==sizeof(pthread_cond_t),""); void ConditionVariable::wait(Mutex& m) @@ -389,6 +389,22 @@ void ConditionVariable::wait(Mutex& m) m.PKlockToDepth(dLock,depth); } +void ConditionVariable::wait(pthread_mutex_t *m) +{ + FastInterruptDisableLock dLock; + Thread *t=Thread::IRQgetCurrentThread(); + CondData listItem(t); + condList.push_back(&listItem); //Putting this thread last on the list (lifo policy) + t->flags.IRQsetCondWait(true); + + unsigned int depth=IRQdoMutexUnlockAllDepthLevels(m); + { + FastInterruptEnableLock eLock(dLock); + Thread::yield(); //Here the wait becomes effective + } + IRQdoMutexLockToDepth(m,dLock,depth); +} + TimedWaitResult ConditionVariable::timedWait(Mutex& m, long long absTime) { //Disallow absolute sleeps with negative or too low values, as the ns2tick() @@ -430,10 +446,36 @@ TimedWaitResult ConditionVariable::timedWait(Mutex& m, long long absTime) return removed ? TimedWaitResult::Timeout : TimedWaitResult::NoTimeout; } -TimedWaitResult ConditionVariable::timedWait(FastMutex& m, long long absTime) +TimedWaitResult ConditionVariable::timedWait(pthread_mutex_t *m, long long absTime) { - return pthreadCondTimedWaitImpl(reinterpret_cast<pthread_cond_t*>(this), - m.get(),absTime)==ETIMEDOUT ? TimedWaitResult::Timeout : TimedWaitResult::NoTimeout; + //Disallow absolute sleeps with negative or too low values, as the ns2tick() + //algorithm in TimeConversion can't handle negative values and may undeflow + //even with very low values due to a negative adjustOffsetNs. As an unlikely + //side effect, very shor sleeps done very early at boot will be extended. + absTime=std::max(absTime,100000LL); + FastInterruptDisableLock dLock; + Thread *t=Thread::IRQgetCurrentThread(); + CondData listItem(t); + condList.push_back(&listItem); //Putting this thread last on the list (lifo policy) + SleepData sleepData(t,absTime); + IRQaddToSleepingList(&sleepData); //Putting this thread on the sleeping list too + t->flags.IRQsetCondWait(true); + + unsigned int depth=IRQdoMutexUnlockAllDepthLevels(m); + { + FastInterruptEnableLock eLock(dLock); + Thread::yield(); //Here the wait becomes effective + } + //Ensure that the thread is removed from both list, as it can be woken by + //either a signal/broadcast (that removes it from condList) or by + //IRQwakeThreads (that removes it from sleeping list). + bool removed=condList.removeFast(&listItem); + IRQremoveFromSleepingList(&sleepData); + + IRQdoMutexLockToDepth(m,dLock,depth); + + //If the thread was still in the cond variable list, it was woken up by a timeout + return removed ? TimedWaitResult::Timeout : TimedWaitResult::NoTimeout; } void ConditionVariable::signal() diff --git a/miosix/kernel/sync.h b/miosix/kernel/sync.h index af1c979f..fde9399b 100644 --- a/miosix/kernel/sync.h +++ b/miosix/kernel/sync.h @@ -461,15 +461,21 @@ public: * Unlock the FastMutex and wait. * If more threads call wait() they must do so specifying the same mutex, * otherwise the behaviour is undefined. - * \param m a locked Mutex + * \param m a locked FastMutex */ void wait(FastMutex& m) { - //Memory layout of ConditionVariable is the same of pthread_cond_t and - //the algorithm would be exactly the same - pthread_cond_wait(reinterpret_cast<pthread_cond_t*>(this),m.get()); + wait(m.get()); } + /** + * Unlock the pthread_mutex_t and wait. + * If more threads call wait() they must do so specifying the same mutex, + * otherwise the behaviour is undefined. + * \param m a locked pthread_mutex_t + */ + void wait(pthread_mutex_t *m); + /** * Unlock the Mutex and wait until woken up or timeout occurs. * If more threads call wait() they must do so specifying the same mutex, @@ -484,11 +490,24 @@ public: * Unlock the FastMutex and wait until woken up or timeout occurs. * If more threads call wait() they must do so specifying the same mutex, * otherwise the behaviour is undefined. - * \param m a locked Mutex + * \param m a locked FastMutex * \param absTime absolute timeout time in nanoseconds * \return whether the return was due to a timout or wakeup */ - TimedWaitResult timedWait(FastMutex& m, long long absTime); + TimedWaitResult timedWait(FastMutex& m, long long absTime) + { + return timedWait(m.get(), absTime); + } + + /** + * Unlock the pthread_mutex_t and wait until woken up or timeout occurs. + * If more threads call wait() they must do so specifying the same mutex, + * otherwise the behaviour is undefined. + * \param m a locked pthread_mutex_t + * \param absTime absolute timeout time in nanoseconds + * \return whether the return was due to a timout or wakeup + */ + TimedWaitResult timedWait(pthread_mutex_t *m, long long absTime); /** * Wakeup one waiting thread. @@ -503,10 +522,13 @@ public: private: //Unwanted methods - ConditionVariable(const ConditionVariable& ); - ConditionVariable& operator= (const ConditionVariable& ); + ConditionVariable(const ConditionVariable&); + ConditionVariable& operator= (const ConditionVariable&); + + //Needs direct access to condList to check if it is empty + friend int ::pthread_cond_destroy(pthread_cond_t *cond); - //Memory layout must be kept in sync with pthread_cond, see wait(FastMutex& m) + //Memory layout must be kept in sync with pthread_cond, see pthread.cpp IntrusiveList<CondData> condList; }; -- GitLab