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