From b33426395e7930837aca2a231f5c24095977ac06 Mon Sep 17 00:00:00 2001
From: Terraneo Federico <fede.tft@miosix.org>
Date: Tue, 25 Apr 2023 23:58:52 +0200
Subject: [PATCH] Implement timedWait at the kernel level. Also provide
 unlocking wait primitives.

---
 miosix/kernel/kernel.cpp | 113 +++++++++++------
 miosix/kernel/kernel.h   | 257 ++++++++++++++++++++++++++++++---------
 miosix/kernel/sync.cpp   | 207 ++++++++-----------------------
 miosix/kernel/sync.h     |   9 --
 4 files changed, 325 insertions(+), 261 deletions(-)

diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp
index a575212a..c5ac40df 100755
--- a/miosix/kernel/kernel.cpp
+++ b/miosix/kernel/kernel.cpp
@@ -260,13 +260,10 @@ bool isKernelRunning()
  * Used by Thread::sleep() and pthread_cond_timedwait() to add a thread to
  * sleeping list. The list is sorted by the wakeupTime field to reduce time
  * required to wake threads during context switch.
- * Also sets thread SLEEP_FLAG. It is labeled IRQ not because it is meant to be
- * used inside an IRQ, but because interrupts must be disabled prior to calling
- * this function.
+ * Interrupts must be disabled prior to calling this function.
  */
-void IRQaddToSleepingList(SleepData *x)
+static void IRQaddToSleepingList(SleepData *x)
 {
-    x->thread->flags.IRQsetSleep(true);
     if(sleepingList.empty() || sleepingList.front()->wakeupTime>=x->wakeupTime)
     {
         sleepingList.push_front(x);
@@ -277,19 +274,6 @@ void IRQaddToSleepingList(SleepData *x)
     }
 }
 
-/**
- * \internal
- * Used by pthread_cond_timedwait() to remove a thread from sleeping list in case that it
- * is woke up by a signal or broadcast.
- * It is labeled IRQ not because it is meant to be
- * used inside an IRQ, but because interrupts must be disabled prior to calling
- * this function.
- */
-void IRQremoveFromSleepingList(SleepData *x)
-{
-    sleepingList.removeFast(x);
-}
-
 /**
  * \internal
  * Called to check if it's time to wake some thread.
@@ -307,9 +291,8 @@ bool IRQwakeThreads(long long currentTime)
     for(auto it=sleepingList.begin();it!=sleepingList.end();)
     {
         if(currentTime<(*it)->wakeupTime) break;
-        (*it)->thread->flags.IRQsetSleep(false); //Wake thread
-        //Reset cond wait flag to wakeup threads in pthread_cond_timedwait() too
-        (*it)->thread->flags.IRQsetCondWait(false);
+        //Wake both threads doing absoluteSleep() and timedWait()
+        (*it)->thread->flags.IRQexitSleepAndWait();
         if(const_cast<Thread*>(runningThread)->getPriority()<(*it)->thread->getPriority())
             result=true;
         it=sleepingList.erase(it);
@@ -396,7 +379,8 @@ void Thread::nanoSleepUntil(long long absoluteTimeNs)
     //the timer isr will wake threads, modifying the sleepingList
     {
         FastInterruptDisableLock lock;
-        IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG
+        d.thread->flags.IRQsetSleep(true); //Sleeping thread: set sleep flag
+        IRQaddToSleepingList(&d);
     }
     // NOTE: There is no need to synchronize the timer (calling IRQsetNextInterrupt)
     // with the list at this point. Because, Thread::yield will make a supervisor
@@ -422,6 +406,32 @@ void Thread::IRQwait()
     const_cast<Thread*>(runningThread)->flags.IRQsetWait(true);
 }
 
+void Thread::PKrestartKernelAndWait(PauseKernelLock& dLock)
+{
+    (void)dLock;
+    //Implemented by upgrading the lock to an interrupt disable one
+    FastInterruptDisableLock dLockIrq;
+    auto savedNesting=kernelRunning;
+    kernelRunning=0;
+    IRQenableIrqAndWaitImpl();
+    if(kernelRunning!=0) errorHandler(UNEXPECTED);
+    kernelRunning=savedNesting;
+}
+
+TimedWaitResult Thread::PKrestartKernelAndTimedWait(PauseKernelLock& dLock,
+        long long absoluteTimeNs)
+{
+    (void)dLock;
+    //Implemented by upgrading the lock to an interrupt disable one
+    FastInterruptDisableLock dLockIrq;
+    auto savedNesting=kernelRunning;
+    kernelRunning=0;
+    auto result=IRQenableIrqAndTimedWaitImpl(absoluteTimeNs);
+    if(kernelRunning!=0) errorHandler(UNEXPECTED);
+    kernelRunning=savedNesting;
+    return result;
+}
+
 void Thread::wakeup()
 {
     //pausing the kernel is not enough because of IRQwait and IRQwakeup
@@ -446,18 +456,14 @@ void Thread::IRQwakeup()
     this->flags.IRQsetWait(false);
 }
 
-Thread *Thread::getCurrentThread()
-{
-    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();
-}
-
 Thread *Thread::IRQgetCurrentThread()
 {
+    //NOTE: this code is currently safe to be called either with interrupt
+    //enabed or not, and with the kernel paused or not, as well as before the
+    //kernel is started, so getCurrentThread() and PKgetCurrentThread() all
+    //directly call here. If introducing changes that break this property, these
+    //three functions may need to be split
+
     //Implementation is the same as getCurrentThread, but to keep a consistent
     //interface this method is duplicated
     Thread *result=const_cast<Thread*>(runningThread);
@@ -823,6 +829,37 @@ void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv)
     errorHandler(UNEXPECTED);
 }
 
+void Thread::IRQenableIrqAndWaitImpl()
+{
+    const_cast<Thread*>(runningThread)->flags.IRQsetWait(true);
+    auto savedNesting=interruptDisableNesting; //For InterruptDisableLock
+    interruptDisableNesting=0;
+    miosix_private::doEnableInterrupts();
+    Thread::yield(); //Here the wait becomes effective
+    miosix_private::doDisableInterrupts();
+    if(interruptDisableNesting!=0) errorHandler(UNEXPECTED);
+    interruptDisableNesting=savedNesting;
+}
+
+TimedWaitResult Thread::IRQenableIrqAndTimedWaitImpl(long long absoluteTimeNs)
+{
+    absoluteTimeNs=std::max(absoluteTimeNs,100000LL);
+    Thread *t=const_cast<Thread*>(runningThread);
+    SleepData sleepData(t,absoluteTimeNs);
+    t->flags.IRQsetWait(true); //timedWait thread: set wait flag
+    IRQaddToSleepingList(&sleepData);
+    auto savedNesting=interruptDisableNesting; //For InterruptDisableLock
+    interruptDisableNesting=0;
+    miosix_private::doEnableInterrupts();
+    Thread::yield(); //Here the wait becomes effective
+    miosix_private::doDisableInterrupts();
+    if(interruptDisableNesting!=0) errorHandler(UNEXPECTED);
+    interruptDisableNesting=savedNesting;
+    bool removed=sleepingList.removeFast(&sleepData);
+    //If the thread was still in the sleeping list, it was woken up by a wakeup()
+    return removed ? TimedWaitResult::NoTimeout : TimedWaitResult::Timeout;
+}
+
 Thread *Thread::allocateIdleThread()
 {
     //NOTE: this function is only called once before the kernel is started, so
@@ -852,21 +889,21 @@ void Thread::ThreadFlags::IRQsetWait(bool waiting)
     Scheduler::IRQwaitStatusHook(this->t);
 }
 
-void Thread::ThreadFlags::IRQsetJoinWait(bool waiting)
+void Thread::ThreadFlags::IRQsetSleep(bool sleeping)
 {
-    if(waiting) flags |= WAIT_JOIN; else flags &= ~WAIT_JOIN;
+    if(sleeping) flags |= SLEEP; else flags &= ~SLEEP;
     Scheduler::IRQwaitStatusHook(this->t);
 }
 
-void Thread::ThreadFlags::IRQsetCondWait(bool waiting)
+void Thread::ThreadFlags::IRQexitSleepAndWait()
 {
-    if(waiting) flags |= WAIT_COND; else flags &= ~WAIT_COND;
+    flags &= ~(WAIT | SLEEP);
     Scheduler::IRQwaitStatusHook(this->t);
 }
 
-void Thread::ThreadFlags::IRQsetSleep(bool sleeping)
+void Thread::ThreadFlags::IRQsetJoinWait(bool waiting)
 {
-    if(sleeping) flags |= SLEEP; else flags &= ~SLEEP;
+    if(waiting) flags |= WAIT_JOIN; else flags &= ~WAIT_JOIN;
     Scheduler::IRQwaitStatusHook(this->t);
 }
 
diff --git a/miosix/kernel/kernel.h b/miosix/kernel/kernel.h
index 342811e0..793dd4bb 100755
--- a/miosix/kernel/kernel.h
+++ b/miosix/kernel/kernel.h
@@ -412,6 +412,15 @@ long long getTime() noexcept;
  */
 long long IRQgetTime() noexcept;
 
+/**
+ * Possible return values of timedWait
+ */
+enum class TimedWaitResult
+{
+    NoTimeout,
+    Timeout
+};
+
 //Forwrd declaration
 class SleepData;
 class MemoryProfiling;
@@ -541,16 +550,19 @@ public:
     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.
+     * This method stops the thread until wakeup() is called.
+     * Ths method is useful to implement any kind of blocking primitive,
+     * including device drivers.
+     *
+     * 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>
+     * This method stops the thread until wakeup() is called.
+     * Ths method is useful to implement any kind of blocking primitive,
+     * including device drivers.
+     *
      * 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
@@ -559,13 +571,148 @@ public:
      * \code
      * disableInterrupts();
      * ...
-     * Thread::IRQwait();//Return immediately
+     * Thread::IRQwait(); //Return immediately
      * enableInterrupts();
-     * Thread::yield();//After this, thread is in wait status
+     * Thread::yield(); //After this, thread is in wait status
      * \endcode
+     *
+     * Consider using IRQenableIrqAndWait() instead.
      */
     static void IRQwait();
 
+    /**
+     * This method stops the thread until wakeup() is called.
+     * Ths method is useful to implement any kind of blocking primitive,
+     * including device drivers.
+     *
+     * NOTE: this method is meant to put the current thread in wait status in a
+     * piece of code where the kernel is paused (preemption disabled).
+     * Preemption will be enabled during the waiting period, and disabled back
+     * before this method returns.
+     *
+     * \param dLock the PauseKernelLock object that was used to disable
+     * preemption in the current context.
+     */
+    static void PKrestartKernelAndWait(PauseKernelLock& dLock);
+
+    /**
+     * This method stops the thread until wakeup() is called.
+     * Ths method is useful to implement any kind of blocking primitive,
+     * including device drivers.
+     *
+     * NOTE: this method is meant to put the current thread in wait status in a
+     * piece of code where interrupts are disbled, interrupts will be enabled
+     * during the waiting period, and disabled back before this method returns.
+     *
+     * \param dLock the InterruptDisableLock object that was used to disable
+     * interrupts in the current context.
+     */
+    static void IRQenableIrqAndWait(InterruptDisableLock& dLock)
+    {
+        (void)dLock; //Common implementation doesn't need it
+        return IRQenableIrqAndWaitImpl();
+    }
+
+    /**
+     * This method stops the thread until wakeup() is called.
+     * Ths method is useful to implement any kind of blocking primitive,
+     * including device drivers.
+     *
+     * NOTE: this method is meant to put the current thread in wait status in a
+     * piece of code where interrupts are disbled, interrupts will be enabled
+     * during the waiting period, and disabled back before this method returns.
+     *
+     * \param dLock the FastInterruptDisableLock object that was used to disable
+     * interrupts in the current context.
+     */
+    static void IRQenableIrqAndWait(FastInterruptDisableLock& dLock)
+    {
+        (void)dLock; //Common implementation doesn't need it
+        return IRQenableIrqAndWaitImpl();
+    }
+
+    /**
+     * This method stops the thread until wakeup() is called or the specified
+     * absolute time in nanoseconds is reached.
+     * Ths method is thus a combined IRQwait() and absoluteSleep(), and is
+     * useful to implement any kind of blocking primitive with timeout,
+     * including device drivers.
+     *
+     * \param absoluteTimeoutNs absolute time after which the wait times out
+     * \return TimedWaitResult::Timeout if the wait timed out
+     */
+    static TimedWaitResult timedWait(long long absoluteTimeNs)
+    {
+        FastInterruptDisableLock dLock;
+        return IRQenableIrqAndTimedWaitImpl(absoluteTimeNs);
+    }
+
+    /**
+     * This method stops the thread until wakeup() is called or the specified
+     * absolute time in nanoseconds is reached.
+     * Ths method is thus a combined IRQwait() and absoluteSleep(), and is
+     * useful to implement any kind of blocking primitive with timeout,
+     * including device drivers.
+     *
+     * NOTE: this method is meant to put the current thread in wait status in a
+     * piece of code where the kernel is paused (preemption disabled).
+     * Preemption will be enabled during the waiting period, and disabled back
+     * before this method returns.
+     *
+     * \param dLock the PauseKernelLock object that was used to disable
+     * preemption in the current context.
+     * \param absoluteTimeoutNs absolute time after which the wait times out
+     * \return TimedWaitResult::Timeout if the wait timed out
+     */
+    static TimedWaitResult PKrestartKernelAndTimedWait(PauseKernelLock& dLock,
+            long long absoluteTimeNs);
+
+    /**
+     * This method stops the thread until wakeup() is called or the specified
+     * absolute time in nanoseconds is reached.
+     * Ths method is thus a combined IRQwait() and absoluteSleep(), and is
+     * useful to implement any kind of blocking primitive with timeout,
+     * including device drivers.
+     *
+     * NOTE: this method is meant to put the current thread in wait status in a
+     * piece of code where interrupts are disbled, interrupts will be enabled
+     * during the waiting period, and disabled back before this method returns.
+     *
+     * \param dLock the InterruptDisableLock object that was used to disable
+     * interrupts in the current context.
+     * \param absoluteTimeoutNs absolute time after which the wait times out
+     * \return TimedWaitResult::Timeout if the wait timed out
+     */
+    static TimedWaitResult IRQenableIrqAndTimedWait(InterruptDisableLock& dLock,
+            long long absoluteTimeNs)
+    {
+        (void)dLock; //Common implementation doesn't need it
+        return IRQenableIrqAndTimedWaitImpl(absoluteTimeNs);
+    }
+
+    /**
+     * This method stops the thread until wakeup() is called or the specified
+     * absolute time in nanoseconds is reached.
+     * Ths method is thus a combined IRQwait() and absoluteSleep(), and is
+     * useful to implement any kind of blocking primitive with timeout,
+     * including device drivers.
+     *
+     * NOTE: this method is meant to put the current thread in wait status in a
+     * piece of code where interrupts are disbled, interrupts will be enabled
+     * during the waiting period, and disabled back before this method returns.
+     *
+     * \param dLock the FastInterruptDisableLock object that was used to disable
+     * interrupts in the current context.
+     * \param absoluteTimeoutNs absolute time after which the wait times out
+     * \return TimedWaitResult::Timeout if the wait timed out
+     */
+    static TimedWaitResult IRQenableIrqAndTimedWait(FastInterruptDisableLock& dLock,
+            long long absoluteTimeNs)
+    {
+        (void)dLock; //Common implementation doesn't need it
+        return IRQenableIrqAndTimedWaitImpl(absoluteTimeNs);
+    }
+
     /**
      * Wakeup a thread.
      * <br>CANNOT be called when the kernel is paused.
@@ -574,28 +721,42 @@ public:
 
     /**
      * Wakeup a thread.
-     * <br>Can be called when the kernel is paused.
+     * <br>Can only 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.
+     * Wakeup a thread.
+     * <br>Can only be called inside an IRQ or when interrupts are disabled.
      */
     void IRQwakeup();
     
     /**
-     * Return a pointer to the Thread class of the current thread.
      * \return a pointer to the current thread.
      *
-     * Can be called when the kernel is paused.
      * Returns a valid pointer also if called before the kernel is started.
      */
-    static Thread *getCurrentThread();
+    static Thread *getCurrentThread()
+    {
+        //Safe to call without disabling IRQ, see implementation
+        return IRQgetCurrentThread();
+    }
 
     /**
-     * Same as get_current_thread(), but meant to be used insida an IRQ, when
-     * interrupts are disabled or when the kernel is paused.
+     * \return a pointer to the current thread.
+     *
+     * Returns a valid pointer also if called before the kernel is started.
+     */
+    static Thread *PKgetCurrentThread()
+    {
+        //Safe to call without disabling IRQ, see implementation
+        return IRQgetCurrentThread();
+    }
+
+    /**
+     * \return a pointer to the current thread.
+     *
+     * Returns a valid pointer also if called before the kernel is started.
      */
     static Thread *IRQgetCurrentThread();
 
@@ -775,25 +936,25 @@ private:
         void IRQsetWait(bool waiting);
 
         /**
-         * Set the wait_join flag of the thread.
+         * Set the sleep flag of the thread.
          * Can only be called with interrupts disabled or within an interrupt.
-         * \param waiting if true the flag will be set, otherwise cleared
+         * \param sleeping if true the flag will be set, otherwise cleared
          */
-        void IRQsetJoinWait(bool waiting);
+        void IRQsetSleep(bool sleeping);
 
         /**
-         * Set wait_cond flag of the thread.
-         * Can only be called with interrupts disabled or within an interrupt.
-         * \param waiting if true the flag will be set, otherwise cleared
+         * Shorthand for IRQsetWait(false); IRQsetSleep(false);
+         * Used by IRQwakeThreads to wake both threads doing absoluteSleep()
+         * and timedWait()
          */
-        void IRQsetCondWait(bool waiting);
+        void IRQexitSleepAndWait();
 
         /**
-         * Set the sleep flag of the thread.
+         * Set the wait_join flag of the thread.
          * Can only be called with interrupts disabled or within an interrupt.
-         * \param sleeping if true the flag will be set, otherwise cleared
+         * \param waiting if true the flag will be set, otherwise cleared
          */
-        void IRQsetSleep(bool sleeping);
+        void IRQsetJoinWait(bool waiting);
 
         /**
          * Set the deleted flag of the thread. This flag can't be cleared.
@@ -858,7 +1019,7 @@ private:
         /**
          * \return true if the thread is in the ready status
          */
-        bool isReady() const { return (flags & 0x67)==0; }
+        bool isReady() const { return (flags & 0x27)==0; }
 
         /**
          * \return true if the thread is detached
@@ -869,11 +1030,6 @@ private:
          * \return true if the thread is waiting a join
          */
         bool isWaitingJoin() const { return flags & WAIT_JOIN; }
-
-        /**
-         * \return true if the thread is waiting on a condition variable
-         */
-        bool isWaitingCond() const { return flags & WAIT_COND; }
         
         /**
          * \return true if the thread is running unprivileged inside a process.
@@ -903,14 +1059,11 @@ private:
 
         ///\internal Thread is waiting for a join
         static const unsigned int WAIT_JOIN=1<<5;
-
-        ///\internal Thread is waiting on a condition variable
-        static const unsigned int WAIT_COND=1<<6;
         
         ///\internal Thread is running in userspace
-        static const unsigned int USERSPACE=1<<7;
+        static const unsigned int USERSPACE=1<<6;
 
-        unsigned short flags;///<\internal flags are stored here
+        unsigned char flags;///<\internal flags are stored here
     };
     
     #ifdef WITH_PROCESSES
@@ -985,6 +1138,16 @@ private:
      */
     static void threadLauncher(void *(*threadfunc)(void*), void *argv);
 
+    /**
+     * Common implementation of all IRQenableIrqAndWait calls
+     */
+    static void IRQenableIrqAndWaitImpl();
+
+    /**
+     * Common implementation of all timedWait calls
+     */
+    static TimedWaitResult IRQenableIrqAndTimedWaitImpl(long long absoluteTimeNs);
+
     /**
      * Allocates the idle thread and makes cur point to it
      * Can only be called before the kernel is started, is called exactly once
@@ -1039,14 +1202,8 @@ private:
     #endif //WITH_CPU_TIME_COUNTER
     
     //friend functions
-    //Need access to status
-    friend void IRQaddToSleepingList(SleepData *);
-    //Need access to status
-    friend void IRQremoveFromSleepingList(SleepData *);
-    //Needs access to status
+    //Needs access to flags
     friend bool IRQwakeThreads(long long);
-    //Needs access to watermark, status, next
-    friend void *idleThread(void *);
     //Needs to create the idle thread
     friend void startKernel();
     //Needs threadLauncher
@@ -1054,24 +1211,12 @@ private:
             unsigned int *, void *);
     //Needs access to priority, savedPriority, mutexLocked and flags.
     friend class Mutex;
-    //Needs access to flags
-    friend class ConditionVariable;
-    //Needs access to flags
-    friend class Semaphore;
     //Needs access to flags, schedData
     friend class PriorityScheduler;
     //Needs access to flags, schedData
     friend class ControlScheduler;
     //Needs access to flags, schedData
     friend class EDFScheduler;
-    //Needs access to flags
-    friend int ::pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *);
-    //Needs access to flags
-    friend int pthreadCondTimedWaitImpl(pthread_cond_t *, pthread_mutex_t *, long long);
-    //Needs access to flags
-    friend int ::pthread_cond_signal(pthread_cond_t *);
-    //Needs access to flags
-    friend int ::pthread_cond_broadcast(pthread_cond_t *);
     //Needs access to cppReent
     friend class CppReentrancyAccessor;
     #ifdef WITH_PROCESSES
diff --git a/miosix/kernel/sync.cpp b/miosix/kernel/sync.cpp
index 2a74c7ba..5d66ed60 100644
--- a/miosix/kernel/sync.cpp
+++ b/miosix/kernel/sync.cpp
@@ -36,9 +36,6 @@ using namespace std;
 
 namespace miosix {
 
-void IRQaddToSleepingList(SleepData *x);
-void IRQremoveFromSleepingList(SleepData *x);
-
 //
 // class Mutex
 //
@@ -50,7 +47,7 @@ Mutex::Mutex(Options opt): owner(nullptr), next(nullptr), waiting()
 
 void Mutex::PKlock(PauseKernelLock& dLock)
 {
-    Thread *p=Thread::getCurrentThread();
+    Thread *p=Thread::PKgetCurrentThread();
     if(owner==nullptr)
     {
         owner=p;
@@ -118,7 +115,7 @@ void Mutex::PKlock(PauseKernelLock& dLock)
 
 void Mutex::PKlockToDepth(PauseKernelLock& dLock, unsigned int depth)
 {
-    Thread *p=Thread::getCurrentThread();
+    Thread *p=Thread::PKgetCurrentThread();
     if(owner==nullptr)
     {
         owner=p;
@@ -188,7 +185,7 @@ void Mutex::PKlockToDepth(PauseKernelLock& dLock, unsigned int depth)
 
 bool Mutex::PKtryLock(PauseKernelLock& dLock)
 {
-    Thread *p=Thread::getCurrentThread();
+    Thread *p=Thread::PKgetCurrentThread();
     if(owner==nullptr)
     {
         owner=p;
@@ -210,7 +207,7 @@ bool Mutex::PKtryLock(PauseKernelLock& dLock)
 
 bool Mutex::PKunlock(PauseKernelLock& dLock)
 {
-    Thread *p=Thread::getCurrentThread();
+    Thread *p=Thread::PKgetCurrentThread();
     if(owner!=p) return false;
 
     if(recursiveDepth>0)
@@ -288,7 +285,7 @@ bool Mutex::PKunlock(PauseKernelLock& dLock)
 
 unsigned int Mutex::PKunlockAllDepthLevels(PauseKernelLock& dLock)
 {
-    Thread *p=Thread::getCurrentThread();
+    Thread *p=Thread::PKgetCurrentThread();
     if(owner!=p) return 0;
 
     //Remove this mutex from the list of mutexes locked by the owner
@@ -324,8 +321,8 @@ unsigned int Mutex::PKunlockAllDepthLevels(PauseKernelLock& dLock)
         while(walk!=nullptr)
         {
             if(walk->waiting.empty()==false)
-                if (pr.mutexLessOp(walk->waiting.front()->getPriority()))
-                    pr = walk->waiting.front()->getPriority();
+                if(pr.mutexLessOp(walk->waiting.front()->getPriority()))
+                    pr=walk->waiting.front()->getPriority();
             walk=walk->next;
         }
         if(pr!=owner->getPriority()) Scheduler::PKsetPriority(owner,pr);
@@ -370,154 +367,74 @@ static_assert(sizeof(ConditionVariable)==sizeof(pthread_cond_t),"");
 
 void ConditionVariable::wait(Mutex& m)
 {
+    WaitToken listItem(Thread::getCurrentThread());
     PauseKernelLock dLock;
-    Thread *t=Thread::getCurrentThread();
-    WaitToken listItem(t);
-    condList.push_back(&listItem); //Add entry to tail of list
-
-    //Unlock mutex and wait
-    {
-        FastInterruptDisableLock l;
-        t->flags.IRQsetCondWait(true);
-    }
-
     unsigned int depth=m.PKunlockAllDepthLevels(dLock);
-    {
-        RestartKernelLock eLock(dLock);
-        Thread::yield(); //Here the wait becomes effective
-    }
+    condList.push_back(&listItem); //Putting this thread last on the list (lifo policy)
+    Thread::PKrestartKernelAndWait(dLock);
+    condList.removeFast(&listItem); //In case of timeout or spurious wakeup
     m.PKlockToDepth(dLock,depth);
 }
 
 void ConditionVariable::wait(pthread_mutex_t *m)
 {
+    WaitToken listItem(Thread::getCurrentThread());
     FastInterruptDisableLock dLock;
-    Thread *t=Thread::IRQgetCurrentThread();
-    WaitToken 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
-    }
+    condList.push_back(&listItem); //Putting this thread last on the list (lifo policy)
+    Thread::IRQenableIrqAndWait(dLock);
+    condList.removeFast(&listItem); //In case of spurious wakeup
     IRQdoMutexLockToDepth(m,dLock,depth);
 }
 
 TimedWaitResult ConditionVariable::timedWait(Mutex& m, long long absTime)
 {
-    //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 short sleeps done very early at boot will be extended.
-    absTime=std::max(absTime,100000LL);
-
+    WaitToken listItem(Thread::getCurrentThread());
     PauseKernelLock dLock;
-    Thread *t=Thread::getCurrentThread();
-    WaitToken listItem(t);
-    condList.push_back(&listItem); //Add entry to tail of list
-    SleepData sleepData(t,absTime);
-    {
-        FastInterruptDisableLock l;
-        IRQaddToSleepingList(&sleepData); //Putting this thread on the sleeping list too
-        t->flags.IRQsetCondWait(true);
-    }
-
-    //Unlock mutex and wait
     unsigned int depth=m.PKunlockAllDepthLevels(dLock);
-    {
-        RestartKernelLock 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);
-    {
-        FastInterruptDisableLock l;
-        IRQremoveFromSleepingList(&sleepData);
-    }
-
+    condList.push_back(&listItem); //Putting this thread last on the list (lifo policy)
+    auto result=Thread::PKrestartKernelAndTimedWait(dLock,absTime);
+    condList.removeFast(&listItem); //In case of timeout or spurious wakeup
     m.PKlockToDepth(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;
+    return result;
 }
 
 TimedWaitResult ConditionVariable::timedWait(pthread_mutex_t *m, long long absTime)
 {
-    //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 short sleeps done very early at boot will be extended.
-    absTime=std::max(absTime,100000LL);
+    WaitToken listItem(Thread::getCurrentThread());
     FastInterruptDisableLock dLock;
-    Thread *t=Thread::IRQgetCurrentThread();
-    WaitToken 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);
-
+    condList.push_back(&listItem); //Putting this thread last on the list (lifo policy)
+    auto result=Thread::IRQenableIrqAndTimedWait(dLock,absTime);
+    condList.removeFast(&listItem); //In case of timeout or spurious wakeup
     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;
+    return result;
 }
 
 bool ConditionVariable::doSignal()
 {
     bool hppw=false;
-    {
-        //Using interruptDisableLock because we need to call IRQsetCondWait
-        //that can only be called with irq disabled, othrwise we would use
-        //PauseKernelLock
-        FastInterruptDisableLock lock;
-        if(condList.empty()) return false;
-        //Remove from list and wakeup
-        Thread *t=condList.front()->thread;
-        condList.pop_front();
-        t->flags.IRQsetCondWait(false);
-        t->flags.IRQsetSleep(false); //Needed due to timedwait
-        //Check for priority issues
-        if(t->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
-            hppw=true;
-    }
+    FastInterruptDisableLock lock; //TODO: Can we pause kernel here?
+    if(condList.empty()) return false;
+    Thread *t=condList.front()->thread;
+    condList.pop_front();
+    t->IRQwakeup();
+    if(t->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+        hppw=true;
     return hppw;
 }
 
 bool ConditionVariable::doBroadcast()
 {
     bool hppw=false;
+    FastInterruptDisableLock lock; //TODO: Can we pause kernel here?
+    while(!condList.empty())
     {
-        //Using interruptDisableLock because we need to call IRQsetCondWait
-        //that can only be called with irq disabled, othrwise we would use
-        //PauseKernelLock
-        FastInterruptDisableLock lock;
-        while(!condList.empty())
-        {
-            //Remove from list and wakeup
-            Thread *t=condList.front()->thread;
-            condList.pop_front();
-            t->flags.IRQsetCondWait(false);
-            t->flags.IRQsetSleep(false); //Needed due to timedwait
-            //Check for priority issues
-            if(t->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
-                hppw=true;
-        }
+        Thread *t=condList.front()->thread;
+        condList.pop_front();
+        t->IRQwakeup();
+        if(t->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+            hppw=true;
     }
     return hppw;
 }
@@ -538,8 +455,7 @@ Thread *Semaphore::IRQsignalNoPreempt()
     WaitToken *cd=fifo.front();
     Thread *t=cd->thread;
     fifo.pop_front();
-    t->flags.IRQsetCondWait(false);
-    t->flags.IRQsetSleep(false); //Needed due to timedwait
+    t->IRQwakeup();
     return t;
 }
 
@@ -582,25 +498,14 @@ void Semaphore::wait()
         return;
     }
     //Otherwise put ourselves in queue and wait
-    Thread *t=Thread::getCurrentThread();
-    WaitToken listItem(t);
+    WaitToken listItem(Thread::IRQgetCurrentThread());
     fifo.push_back(&listItem); //Add entry to tail of list
-    t->flags.IRQsetCondWait(true);
-    {
-        FastInterruptEnableLock eLock(dLock);
-        //The wait becomes effective here
-        Thread::yield();
-    }
+    Thread::IRQenableIrqAndWait(dLock);
+    fifo.removeFast(&listItem); //In case of spurious wakeup
 }
 
 TimedWaitResult Semaphore::timedWait(long long absTime)
 {
-    //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 short sleeps done very early at boot will be extended.
-    absTime=std::max(absTime,100000LL);
-
     //Global interrupt lock because Semaphore is IRQ-safe
     FastInterruptDisableLock dLock;
     //If the counter is positive, decrement it and we're done
@@ -609,26 +514,12 @@ TimedWaitResult Semaphore::timedWait(long long absTime)
         count--;
         return TimedWaitResult::NoTimeout;
     }
-    //Otherwise put ourselves in queue...
-    Thread *t=Thread::getCurrentThread();
-    WaitToken listItem(t);
-    fifo.push_back(&listItem);
-    //...and simultaneously to sleep
-    SleepData sleepData(t,absTime);
-    IRQaddToSleepingList(&sleepData);
-    t->flags.IRQsetCondWait(true);
-    {
-        FastInterruptEnableLock eLock(dLock);
-        //Wait/sleep becomes effective here
-        Thread::yield();
-    }
-    
-    //We got woken up by either the sleep or the wait. Ensure that the thread
-    //is removed from both the wait list and the sleep list.
-    bool removed=fifo.removeFast(&listItem);
-    IRQremoveFromSleepingList(&sleepData);
-    //If we were still in the fifo, we were woken up by a timeout
-    return removed ? TimedWaitResult::Timeout : TimedWaitResult::NoTimeout;
+    //Otherwise put ourselves in queue and wait
+    WaitToken listItem(Thread::IRQgetCurrentThread());
+    fifo.push_back(&listItem); //Add entry to tail of list
+    auto result=Thread::IRQenableIrqAndTimedWait(dLock,absTime);
+    fifo.removeFast(&listItem); //In case of timeout or spurious wakeup
+    return result;
 }
 
 } //namespace miosix
diff --git a/miosix/kernel/sync.h b/miosix/kernel/sync.h
index 9ee9a5f0..bc991312 100644
--- a/miosix/kernel/sync.h
+++ b/miosix/kernel/sync.h
@@ -398,15 +398,6 @@ public:
     Thread *thread; ///<\internal Thread that is waiting
 };
 
-/**
- * Possible return values of timedWait
- */
-enum class TimedWaitResult
-{
-    NoTimeout,
-    Timeout
-};
-
 /**
  * A condition variable class for thread synchronization, available from
  * Miosix 1.53.<br>
-- 
GitLab