diff --git a/miosix/config/miosix_settings.h b/miosix/config/miosix_settings.h index 7b75d5f9ad3aa10675fda2f5dbe4db11b5dbb9db..e48e39e79e57ce6e4521617512f5a3530a94f0b8 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 b7e36a64f8420955e99d95a3d2b1d38de83aa959..204f93b5142b3bfe66710460a9165218bb069752 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 36b76fdfdae15608cce72e897bd9083fb8df9247..91a0ce239498904e7447967996e4b82f0dccd8a8 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 999b711c4c45e843fc588c7e10edbf02855a09a9..218a7b36f530eab0462fdbb716885c7f91a343d0 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 371677d6b31266d798379a5ee7e9755e3587b908..bed3f28811205199a6bfe1cf32e9b90391a068da 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