diff --git a/miosix/e20/e20.h b/miosix/e20/e20.h index 9eb191d3a0f9e44713be70107ff2578fb77ac889..bafe6258f2ab59fb7951b784686054f9800620c8 100644 --- a/miosix/e20/e20.h +++ b/miosix/e20/e20.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2012, 2013, 2014, 2015, 2016 by Terraneo Federico * + * Copyright (C) 2012 - 2023 by Terraneo Federico * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -27,8 +27,7 @@ //Miosix event based API -#ifndef E20_H -#define E20_H +#pragma once #include <list> #include <functional> @@ -104,7 +103,7 @@ private: EventQueue(const EventQueue&); EventQueue& operator= (const EventQueue&); - std::list<std::function<void ()> > events; ///< Event queue + std::list<std::function<void ()>> events; ///< Event queue mutable FastMutex m; ///< Mutex for synchronisation ConditionVariable cv; ///< Condition variable for synchronisation }; @@ -120,8 +119,7 @@ protected: /** * Constructor. */ - FixedEventQueueBase() : put(0), get(0), n(0), waitingGet(0), waitingPut(0) - {} + FixedEventQueueBase() {} /** * Post an event. Blocks if event queue is full. @@ -137,11 +135,12 @@ protected: * \param event event to post * \param events pointer to event queue * \param size event queue size - * \param hppw set to true if a higher priority thread is awakened, - * otherwise the variable is not modified + * \param hppw if not null set to true if a higher priority thread is + * awakened, otherwise the variable is not modified + * \return false if there was no space in the queue */ bool IRQpostImpl(Callback<SlotSize>& event, Callback<SlotSize> *events, - unsigned int size, bool *hppw=0); + unsigned int size, bool *hppw=nullptr); /** * This function blocks waiting for events being posted, and when available @@ -174,20 +173,19 @@ protected: private: /** - * To allow multiple threads waiting on put and get + * \internal Element of a thread waiting list */ - struct WaitingList + class WaitToken : public IntrusiveListItem { - WaitingList *next; ///< Pointer to next element of the list - Thread *t; ///< Thread waiting - bool token; ///< To tolerate spurious wakeups + public: + WaitToken(Thread *thread) : thread(thread) {} + Thread *thread; ///<\internal Waiting thread and spurious wakeup token }; - unsigned int put; ///< Put position into events - unsigned int get; ///< Get position into events - unsigned int n; ///< Number of occupied event slots - WaitingList *waitingGet; ///< List of threads waiting to get an event - WaitingList *waitingPut; ///< List of threads waiting to put an event + unsigned int put=0; ///< Put position into events + unsigned int get=0; ///< Get position into events + unsigned int n=0; ///< Number of occupied event slots + IntrusiveList<WaitToken> waitingGet, waitingPut; ///< Waiting on get/put }; template<unsigned SlotSize> @@ -197,23 +195,13 @@ void FixedEventQueueBase<SlotSize>::postImpl(Callback<SlotSize>& event, //Not FastInterruptDisableLock as the operator= of the bound //parameters of the Callback may allocate InterruptDisableLock dLock; - while(n>=size) + while(IRQpostImpl(event,events,size)==false) { - WaitingList w; - w.token=false; - w.t=Thread::IRQgetCurrentThread(); - w.next=waitingPut; - waitingPut=&w; - while(w.token==false) - { - Thread::IRQwait(); - { - InterruptEnableLock eLock(dLock); - Thread::yield(); - } - } + WaitToken w(Thread::IRQgetCurrentThread()); + waitingPut.push_back(&w); + //w.thread must be set to nullptr to protect against spurious wakeups + while(w.thread) Thread::IRQenableIrqAndWait(dLock); } - IRQpostImpl(event,events,size); } template<unsigned SlotSize> @@ -224,14 +212,14 @@ bool FixedEventQueueBase<SlotSize>::IRQpostImpl(Callback<SlotSize>& event, events[put]=event; //This may allocate memory if(++put>=size) put=0; n++; - if(waitingGet) + if(waitingGet.empty()==false) { - Thread *t=Thread::IRQgetCurrentThread(); - if(hppw && waitingGet->t->IRQgetPriority()>t->IRQgetPriority()) + Thread *t=waitingGet.front()->thread; + waitingGet.front()->thread=nullptr; + waitingGet.pop_front(); + t->IRQwakeup(); + if(hppw && t->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) *hppw=true; - waitingGet->token=true; - waitingGet->t->IRQwakeup(); - waitingGet=waitingGet->next; } return true; } @@ -247,28 +235,19 @@ void FixedEventQueueBase<SlotSize>::runImpl(Callback<SlotSize> *events, { while(n<=0) { - WaitingList w; - w.token=false; - w.t=Thread::IRQgetCurrentThread(); - w.next=waitingGet; - waitingGet=&w; - while(w.token==false) - { - Thread::IRQwait(); - { - InterruptEnableLock eLock(dLock); - Thread::yield(); - } - } + WaitToken w(Thread::IRQgetCurrentThread()); + waitingGet.push_back(&w); + //w.thread must be set to nullptr to protect against spurious wakeups + while(w.thread) Thread::IRQenableIrqAndWait(dLock); } Callback<SlotSize> f=events[get]; //This may allocate memory if(++get>=size) get=0; n--; - if(waitingPut) + if(waitingPut.empty()==false) { - waitingPut->token=true; - waitingPut->t->IRQwakeup(); - waitingPut=waitingPut->next; + waitingPut.front()->thread->IRQwakeup(); + waitingPut.front()->thread=nullptr; + waitingPut.pop_front(); } { InterruptEnableLock eLock(dLock); @@ -290,11 +269,11 @@ void FixedEventQueueBase<SlotSize>::runOneImpl(Callback<SlotSize> *events, f=events[get]; //This may allocate memory if(++get>=size) get=0; n--; - if(waitingPut) + if(waitingPut.empty()==false) { - waitingPut->token=true; - waitingPut->t->IRQwakeup(); - waitingPut=waitingPut->next; + waitingPut.front()->thread->IRQwakeup(); + waitingPut.front()->thread=nullptr; + waitingPut.pop_front(); } } f(); @@ -373,7 +352,7 @@ public: * the restriction that they need to be callable with interrupts disabled * so they must not open files, print, ... * - * If the call is made from within an InterruptDisableLock the copy + * \warning If the call is made from within an InterruptDisableLock the copy * constructors can allocate memory, while if the call is made from an * interrupt handler or a FastInterruptFisableLock memory allocation is * forbidden. @@ -395,7 +374,7 @@ public: * the restriction that they need to be callable with interrupts disabled * so they must not open files, print, ... * - * If the call is made from within an InterruptDisableLock the copy + * \warning If the call is made from within an InterruptDisableLock the copy * constructors can allocate memory, while if the call is made from an * interrupt handler or a FastInterruptFisableLock memory allocation is * forbidden. @@ -456,5 +435,3 @@ private: }; } //namespace miosix - -#endif //E20_H