OpenRTX/lib/miosix-kernel/miosix/e20/e20.h

461 wiersze
15 KiB
C++

/***************************************************************************
* Copyright (C) 2012, 2013, 2014, 2015, 2016 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 *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* As a special exception, if other files instantiate templates or use *
* macros or inline functions from this file, or you compile this file *
* and link it with other works to produce a work based on this file, *
* this file does not by itself cause the resulting work to be covered *
* by the GNU General Public License. However the source code for this *
* file must still be made available in accordance with the GNU General *
* Public License. This exception does not invalidate any other reasons *
* why a work based on this file might be covered by the GNU General *
* Public License. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, see <http://www.gnu.org/licenses/> *
***************************************************************************/
//Miosix event based API
#ifndef E20_H
#define E20_H
#include <list>
#include <functional>
#include <miosix.h>
#include "callback.h"
namespace miosix {
/**
* A variable sized event queue.
*
* Makes use of heap allocations and as such it is not possible to post events
* from within interrupt service routines. For this, use FixedEventQueue.
*
* This class acts as a synchronization point, multiple threads can post
* events, and multiple threads can call run() or runOne() (thread pooling).
*
* Events are function that are posted by a thread through post() but executed
* in the context of the thread that calls run() or runOne()
*/
class EventQueue
{
public:
/**
* Constructor
*/
EventQueue() {}
/**
* Post an event to the queue. This function never blocks.
*
* \param event function function to be called in the thread that calls
* run() or runOne(). Bind can be used to bind parameters to the function.
* \throws std::bad_alloc if there is not enough heap memory
*/
void post(std::function<void ()> event);
/**
* This function blocks waiting for events being posted, and when available
* it calls the event function. To return from this event loop an event
* function must throw an exception.
*
* \throws any exception that is thrown by the event functions
*/
void run();
/**
* Run at most one event. This function does not block.
*
* \throws any exception that is thrown by the event functions
*/
void runOne();
/**
* \return the number of events in the queue
*/
unsigned int size() const
{
Lock<FastMutex> l(m);
return events.size();
}
/**
* \return true if the queue has no events
*/
bool empty() const
{
Lock<FastMutex> l(m);
return events.empty();
}
private:
EventQueue(const EventQueue&);
EventQueue& operator= (const EventQueue&);
std::list<std::function<void ()> > events; ///< Event queue
mutable FastMutex m; ///< Mutex for synchronisation
ConditionVariable cv; ///< Condition variable for synchronisation
};
/**
* This class is to extract from FixedEventQueue code that
* does not depend on the NumSlots template parameters.
*/
template<unsigned SlotSize>
class FixedEventQueueBase
{
protected:
/**
* Constructor.
*/
FixedEventQueueBase() : put(0), get(0), n(0), waitingGet(0), waitingPut(0)
{}
/**
* Post an event. Blocks if event queue is full.
* \param event event to post
* \param events pointer to event queue
* \param size event queue size
*/
void postImpl(Callback<SlotSize>& event, Callback<SlotSize> *events,
unsigned int size);
/**
* Post an event from an interrupt, or with interrupts disabled.
* \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
*/
bool IRQpostImpl(Callback<SlotSize>& event, Callback<SlotSize> *events,
unsigned int size, bool *hppw=0);
/**
* This function blocks waiting for events being posted, and when available
* it calls the event function. To return from this event loop an event
* function must throw an exception.
*
* \param events pointer to event queue
* \param size event queue size
* \throws any exception that is thrown by the event functions
*/
void runImpl(Callback<SlotSize> *events, unsigned int size);
/**
* Run at most one event. This function does not block.
*
* \param events pointer to event queue
* \param size event queue size
* \throws any exception that is thrown by the event functions
*/
void runOneImpl(Callback<SlotSize> *events, unsigned int size);
/**
* \return the number of events in the queue
*/
unsigned int sizeImpl() const
{
FastInterruptDisableLock dLock;
return n;
}
private:
/**
* To allow multiple threads waiting on put and get
*/
struct WaitingList
{
WaitingList *next; ///< Pointer to next element of the list
Thread *t; ///< Thread waiting
bool token; ///< To tolerate spurious wakeups
};
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
};
template<unsigned SlotSize>
void FixedEventQueueBase<SlotSize>::postImpl(Callback<SlotSize>& event,
Callback<SlotSize> *events, unsigned int size)
{
//Not FastInterruptDisableLock as the operator= of the bound
//parameters of the Callback may allocate
InterruptDisableLock dLock;
while(n>=size)
{
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();
}
}
}
IRQpostImpl(event,events,size);
}
template<unsigned SlotSize>
bool FixedEventQueueBase<SlotSize>::IRQpostImpl(Callback<SlotSize>& event,
Callback<SlotSize> *events, unsigned int size, bool *hppw)
{
if(n>=size) return false;
events[put]=event; //This may allocate memory
if(++put>=size) put=0;
n++;
if(waitingGet)
{
Thread *t=Thread::IRQgetCurrentThread();
if(hppw && waitingGet->t->IRQgetPriority()>t->IRQgetPriority())
*hppw=true;
waitingGet->token=true;
waitingGet->t->IRQwakeup();
waitingGet=waitingGet->next;
}
return true;
}
template<unsigned SlotSize>
void FixedEventQueueBase<SlotSize>::runImpl(Callback<SlotSize> *events,
unsigned int size)
{
//Not FastInterruptDisableLock as the operator= of the bound
//parameters of the Callback may allocate
InterruptDisableLock dLock;
for(;;)
{
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();
}
}
}
Callback<SlotSize> f=events[get]; //This may allocate memory
if(++get>=size) get=0;
n--;
if(waitingPut)
{
waitingPut->token=true;
waitingPut->t->IRQwakeup();
waitingPut=waitingPut->next;
}
{
InterruptEnableLock eLock(dLock);
f();
}
}
}
template<unsigned SlotSize>
void FixedEventQueueBase<SlotSize>::runOneImpl(Callback<SlotSize> *events,
unsigned int size)
{
Callback<SlotSize> f;
{
//Not FastInterruptDisableLock as the operator= of the bound
//parameters of the Callback may allocate
InterruptDisableLock dLock;
if(n<=0) return;
f=events[get]; //This may allocate memory
if(++get>=size) get=0;
n--;
if(waitingPut)
{
waitingPut->token=true;
waitingPut->t->IRQwakeup();
waitingPut=waitingPut->next;
}
}
f();
}
/**
* A fixed size event queue.
*
* This guarantees it makes no use of the heap, therefore events can be posted
* also from within interrupt handlers. This simplifies the development of
* device drivers.
*
* This class acts as a synchronization point, multiple threads (and IRQs) can
* post events, and multiple threads can call run() or runOne()
* (thread pooling).
*
* Events are function that are posted by a thread through post() but executed
* in the context of the thread that calls run() or runOne()
*
* \param NumSlots maximum queue length
* \param SlotSize size of the Callback objects. This limits the maximum number
* of parameters that can be bound to a function. If you get compile-time
* errors in callback.h, consider increasing this value. The default is 20
* bytes, which is enough to bind a member function pointer, a "this" pointer
* and two byte or pointer sized parameters.
*/
template<unsigned NumSlots, unsigned SlotSize=20>
class FixedEventQueue : private FixedEventQueueBase<SlotSize>
{
public:
/**
* Constructor.
*/
FixedEventQueue() {}
/**
* Post an event, blocking if the event queue is full.
*
* \param event function function to be called in the thread that calls
* run() or runOne(). Bind can be used to bind parameters to the function.
* Unlike with the EventQueue, the operator= of the bound parameters have
* the restriction that they need to be callable from inside a
* InterruptDisableLock without causing undefined behaviour, so they
* must not, open files, print, ... but can allocate memory.
*/
void post(Callback<SlotSize> event)
{
this->postImpl(event,events,NumSlots);
}
/**
* Post an event in the queue, or return if the queue was full.
*
* \param event function function to be called in the thread that calls
* run() or runOne(). Bind can be used to bind parameters to the function.
* Unlike with the EventQueue, the operator= of the bound parameters have
* the restriction that they need to be callable from inside a
* InterruptDisableLock without causing undefined behaviour, so they
* must not open files, print, ... but can allocate memory.
* \return false if there was no space in the queue
*/
bool postNonBlocking(Callback<SlotSize> event)
{
InterruptDisableLock dLock;
return this->IRQpostImpl(event,events,NumSlots);
}
/**
* Post an event in the queue, or return if the queue was full.
* Can be called only with interrupts disabled or within an interrupt
* handler, allowing device drivers to post an event to a thread.
*
* \param event function function to be called in the thread that calls
* run() or runOne(). Bind can be used to bind parameters to the function.
* Unlike with the EventQueue, the operator= of the bound parameters have
* 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
* constructors can allocate memory, while if the call is made from an
* interrupt handler or a FastInterruptFisableLock memory allocation is
* forbidden.
* \return false if there was no space in the queue
*/
bool IRQpost(Callback<SlotSize> event)
{
return this->IRQpostImpl(event,events,NumSlots);
}
/**
* Post an event in the queue, or return if the queue was full.
* Can be called only with interrupts disabled or within an interrupt
* handler, allowing device drivers to post an event to a thread.
*
* \param event function function to be called in the thread that calls
* run() or runOne(). Bind can be used to bind parameters to the function.
* Unlike with the EventQueue, the operator= of the bound parameters have
* 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
* constructors can allocate memory, while if the call is made from an
* interrupt handler or a FastInterruptFisableLock memory allocation is
* forbidden.
* \param hppw returns true if a higher priority thread was awakened as
* part of posting the event. Can be used inside an IRQ to call the
* scheduler.
* \return false if there was no space in the queue
*/
bool IRQpost(Callback<SlotSize> event, bool& hppw)
{
hppw=false;
return this->IRQpostImpl(event,events,NumSlots,&hppw);
}
/**
* This function blocks waiting for events being posted, and when available
* it calls the event function. To return from this event loop an event
* function must throw an exception.
*
* \throws any exception that is thrown by the event functions
*/
void run()
{
this->runImpl(events,NumSlots);
}
/**
* Run at most one event. This function does not block.
*
* \throws any exception that is thrown by the event functions
*/
void runOne()
{
this->runOneImpl(events,NumSlots);
}
/**
* \return the number of events in the queue
*/
unsigned int size() const
{
return this->sizeImpl();
}
/**
* \return true if the queue has no events
*/
unsigned int empty() const
{
return this->sizeImpl()==0;
}
private:
FixedEventQueue(const FixedEventQueue&);
FixedEventQueue& operator= (const FixedEventQueue&);
Callback<SlotSize> events[NumSlots]; ///< Fixed size queue of events
};
} //namespace miosix
#endif //E20_H