From 1150aea6561002d017067a29b35f47dc9f675fd7 Mon Sep 17 00:00:00 2001 From: Terraneo Federico <fede.tft@miosix.org> Date: Tue, 21 Jan 2025 18:21:31 +0100 Subject: [PATCH] Add support for pthread_key API, disabled by default --- miosix/config/miosix_settings.h | 6 +++ miosix/kernel/kernel.cpp | 19 ++++++--- miosix/kernel/kernel.h | 27 +++++++++++++ miosix/kernel/pthread.cpp | 71 +++++++++++++++++++++++++++++++++ miosix/kernel/pthread_private.h | 8 ++++ 5 files changed, 126 insertions(+), 5 deletions(-) diff --git a/miosix/config/miosix_settings.h b/miosix/config/miosix_settings.h index 7b75d5f9..e48e39e7 100644 --- a/miosix/config/miosix_settings.h +++ b/miosix/config/miosix_settings.h @@ -251,6 +251,12 @@ const unsigned char MAIN_PRIORITY=1; const unsigned int MAX_TIME_SLICE=1000000; #endif //SCHED_TYPE_PRIORITY +/// Enable support for pthread_key_create/pthread_key_delete/pthread_getspecific +//#define WITH_PTHREAD_KEYS + +/// Maximum number of concurrently existing pthread keys +const unsigned int MAX_PTHREAD_KEYS=2; + // // Other low level kernel options. There is usually no need to modify these. diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index b7e36a64..204f93b5 100755 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -39,6 +39,7 @@ #include "interfaces_private/os_timer.h" #include "interfaces_private/sleep.h" #include "timeconversion.h" +#include "pthread_private.h" #include <stdexcept> #include <algorithm> #include <limits> @@ -763,6 +764,9 @@ Thread::Thread(unsigned int *watermark, unsigned int stacksize, proc=kernel; userCtxsave=nullptr; #endif //WITH_PROCESSES + #ifdef WITH_PTHREAD_KEYS + memset(pthreadKeyValues,0,sizeof(pthreadKeyValues)); + #endif //WITH_PTHREAD_KEYS } Thread::~Thread() @@ -833,24 +837,29 @@ void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv) errorLog("***An exception propagated through a thread\n"); } #endif //__NO_EXCEPTIONS + Thread* cur=const_cast<Thread*>(runningThread); + + #ifdef WITH_PTHREAD_KEYS + callPthreadKeyDestructors(cur->pthreadKeyValues); + #endif //WITH_PTHREAD_KEYS //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(); + cur->flags.IRQsetDeleted(); - if(const_cast<Thread*>(runningThread)->flags.isDetached()==false) + if(cur->flags.isDetached()==false) { //If thread is joinable, handle join - if(runningThread->joinData.waitingForJoin!=nullptr) + if(cur->joinData.waitingForJoin!=nullptr) { //Wake thread - runningThread->joinData.waitingForJoin->flags.IRQsetJoinWait(false); + cur->joinData.waitingForJoin->flags.IRQsetJoinWait(false); } //Set result - runningThread->joinData.result=result; + cur->joinData.result=result; } else { //If thread is detached, memory can be deallocated immediately existDeleted=true; diff --git a/miosix/kernel/kernel.h b/miosix/kernel/kernel.h index 36b76fdf..91a0ce23 100755 --- a/miosix/kernel/kernel.h +++ b/miosix/kernel/kernel.h @@ -927,6 +927,29 @@ public: #endif //WITH_PROCESSES + #ifdef WITH_PTHREAD_KEYS + + /** + * \internal, used to implement pthread_setspecific + */ + int setPthreadKeyValue(pthread_key_t key, void * const value) + { + if(key>=MAX_PTHREAD_KEYS) return EINVAL; + pthreadKeyValues[key]=value; + return 0; + } + + /** + * \internal, used to implement pthread_getspecific + */ + void *getPthreadKeyValue(pthread_key_t key) + { + if(key>=MAX_PTHREAD_KEYS) return nullptr; //No way to report error + return pthreadKeyValues[key]; + } + + #endif //WITH_PTHREAD_KEYS + //Unwanted methods Thread(const Thread& p) = delete; Thread& operator = (const Thread& p) = delete; @@ -1227,6 +1250,10 @@ private: #ifdef WITH_CPU_TIME_COUNTER CPUTimeCounterPrivateThreadData timeCounterData; #endif //WITH_CPU_TIME_COUNTER + #ifdef WITH_PTHREAD_KEYS + ///Thread local values associated to pthread keys + void *pthreadKeyValues[MAX_PTHREAD_KEYS]; + #endif //WITH_PTHREAD_KEYS //friend functions //Needs access to flags diff --git a/miosix/kernel/pthread.cpp b/miosix/kernel/pthread.cpp index 999b711c..218a7b36 100644 --- a/miosix/kernel/pthread.cpp +++ b/miosix/kernel/pthread.cpp @@ -390,5 +390,76 @@ int pthread_once(pthread_once_t *once, void (*func)()) int pthread_setcancelstate(int state, int *oldstate) { return 0; } //Stub +#ifdef WITH_PTHREAD_KEYS + +typedef void (*destructor_type)(void *); + +static FastMutex pthreadKeyMutex; +static bool keySlotUsed[MAX_PTHREAD_KEYS]={false}; +static destructor_type keyDestructor[MAX_PTHREAD_KEYS]={nullptr}; + +int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) +{ + Lock<FastMutex> l(pthreadKeyMutex); + for(unsigned int i=0;i<MAX_PTHREAD_KEYS;i++) + { + if(keySlotUsed[i]) continue; + keySlotUsed[i]=true; + keyDestructor[i]=destructor; + *key=i; + return 0; + } + return EAGAIN; +} + +int pthread_key_delete(pthread_key_t key) +{ + Lock<FastMutex> l(pthreadKeyMutex); + if(keySlotUsed[key]==false) return EINVAL; + keySlotUsed[key]=false; + keyDestructor[key]=nullptr; + return 0; +} + +int pthread_setspecific(pthread_key_t key, const void *value) +{ + //This cast because POSIX misunderstood how const works + auto v = const_cast<void * const>(value); + return Thread::getCurrentThread()->setPthreadKeyValue(key,v); +} + +void *pthread_getspecific(pthread_key_t key) +{ + return Thread::getCurrentThread()->getPthreadKeyValue(key); +} + +#endif //WITH_PTHREAD_KEYS + } //extern "C" +#ifdef WITH_PTHREAD_KEYS + +namespace miosix { + +void callPthreadKeyDestructors(void *pthreadKeyValues[MAX_PTHREAD_KEYS]) +{ + Lock<FastMutex> l(pthreadKeyMutex); + for(unsigned int i=0;i<MAX_PTHREAD_KEYS;i++) + { + if(pthreadKeyValues[i]==nullptr) continue; //No value, nothing to do + //POSIX wants destructor called after key value is set to nullptr + auto temp=pthreadKeyValues[i]; + pthreadKeyValues[i]=nullptr; + if(keyDestructor[i]==nullptr) continue; //No destructor, discard temp + keyDestructor[i](temp); + } + //NOTE: the POSIX spec state that calling a destructor may set another key + //and we should play whack-a-mole calling again destructors till all values + //become nulllptr, which may lead to an infinite loop or we may choose to + //stop after PTHREAD_DESTRUCTOR_ITERATIONS. For now we don't do it, and act + //as if PTHREAD_DESTRUCTOR_ITERATIONS is 1 on Miosix +} + +} //namespace miosix + +#endif //WITH_PTHREAD_KEYS diff --git a/miosix/kernel/pthread_private.h b/miosix/kernel/pthread_private.h index 371677d6..bed3f288 100644 --- a/miosix/kernel/pthread_private.h +++ b/miosix/kernel/pthread_private.h @@ -167,4 +167,12 @@ static inline unsigned int IRQdoMutexUnlockAllDepthLevels(pthread_mutex_t *mutex return result; } +#ifdef WITH_PTHREAD_KEYS +/** + * Called at thread exit to call destructors for pthread keys + * \param pthreadKeyValues thread-local pthread_key values + */ +void callPthreadKeyDestructors(void *pthreadKeyValues[MAX_PTHREAD_KEYS]); +#endif //WITH_PTHREAD_KEYS + } //namespace miosix -- GitLab