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

527 wiersze
15 KiB
C++

/***************************************************************************
* Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 *
* 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 kernel
#ifndef SYNC_H
#define SYNC_H
#include "kernel.h"
#include <vector>
namespace miosix {
/**
* \addtogroup Sync
* \{
*/
/**
* Fast mutex without support for priority inheritance
*/
class FastMutex
{
public:
/**
* Mutex options, passed to the constructor to set additional options.<br>
* The DEFAULT option indicates the default Mutex type.
*/
enum Options
{
DEFAULT, ///< Default mutex
RECURSIVE ///< Mutex is recursive
};
/**
* Constructor, initializes the mutex.
*/
FastMutex(Options opt=DEFAULT)
{
if(opt==RECURSIVE)
{
pthread_mutexattr_t temp;
pthread_mutexattr_init(&temp);
pthread_mutexattr_settype(&temp,PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&impl,&temp);
pthread_mutexattr_destroy(&temp);
} else pthread_mutex_init(&impl,NULL);
}
/**
* Locks the critical section. If the critical section is already locked,
* the thread will be queued in a wait list.
*/
void lock()
{
pthread_mutex_lock(&impl);
}
/**
* Acquires the lock only if the critical section is not already locked by
* other threads. Attempting to lock again a recursive mutex will fail, and
* the mutex' lock count will not be incremented.
* \return true if the lock was acquired
*/
bool tryLock()
{
return pthread_mutex_trylock(&impl)==0;
}
/**
* Unlocks the critical section.
*/
void unlock()
{
pthread_mutex_unlock(&impl);
}
/**
* \internal
* \return the FastMutex implementation defined mutex type
*/
pthread_mutex_t *get()
{
return &impl;
}
/**
* Destructor
*/
~FastMutex()
{
pthread_mutex_destroy(&impl);
}
private:
FastMutex(const FastMutex&);
FastMutex& operator= (const FastMutex&);
pthread_mutex_t impl;
};
//Forward declaration
class ConditionVariable;
/**
* A mutex class with support for priority inheritance. If a thread tries to
* enter a critical section which is not free, it will be put to sleep and
* added to a queue of sleeping threads, ordered by priority. The thread that
* is into the critical section inherits the highest priority among the threads
* that are waiting if it is higher than its original priority.<br>
* This mutex is meant to be a static or global class. Dynamically creating a
* mutex with new or on the stack must be done with care, to avoid deleting a
* locked mutex, and to avoid situations where a thread tries to lock a
* deleted mutex.<br>
*/
class Mutex
{
public:
/**
* Mutex options, passed to the constructor to set additional options.<br>
* The DEFAULT option indicates the default Mutex type.
*/
enum Options
{
DEFAULT, ///< Default mutex
RECURSIVE ///< Mutex is recursive
};
/**
* Constructor, initializes the mutex.
*/
Mutex(Options opt=DEFAULT);
/**
* Locks the critical section. If the critical section is already locked,
* the thread will be queued in a wait list.
*/
void lock()
{
PauseKernelLock dLock;
PKlock(dLock);
}
/**
* Acquires the lock only if the critical section is not already locked by
* other threads. Attempting to lock again a recursive mutex will fail, and
* the mutex' lock count will not be incremented.
* \return true if the lock was acquired
*/
bool tryLock()
{
PauseKernelLock dLock;
return PKtryLock(dLock);
}
/**
* Unlocks the critical section.
*/
void unlock()
{
#ifdef SCHED_TYPE_EDF
bool hppw;
{
PauseKernelLock dLock;
hppw=PKunlock(dLock);
}
if(hppw) Thread::yield();//The other thread might have a closer deadline
#else
{
PauseKernelLock dLock;
PKunlock(dLock);
}
#endif //SCHED_TYPE_EDF
}
private:
//Unwanted methods
Mutex(const Mutex& s);///< No public copy constructor
Mutex& operator = (const Mutex& s);///< No publc operator =
//Uses default destructor
/**
* Lock mutex, can be called only with kernel paused one level deep
* (pauseKernel calls can be nested). If another thread holds the mutex,
* this call will restart the kernel and wait (that's why the kernel must
* be paused one level deep).<br>
* \param dLock the PauseKernelLock instance that paused the kernel.
*/
void PKlock(PauseKernelLock& dLock);
/**
* Lock mutex to a given depth, can be called only with kernel paused one
* level deep (pauseKernel calls can be nested). If another thread holds the
* mutex, this call will restart the kernel and wait (that's why the kernel
* must be paused one level deep).<br>
* If the mutex is not recursive the mutex is locked only one level deep
* regardless of the depth value.
* \param dLock the PauseKernelLock instance that paused the kernel.
* \param depth recursive depth at which the mutex will be locked. Zero
* means the mutex is locked one level deep (as if lock() was called once),
* one means two levels deep, etc.
*/
void PKlockToDepth(PauseKernelLock& dLock, unsigned int depth);
/**
* Acquires the lock only if the critical section is not already locked by
* other threads. Attempting to lock again a recursive mutex will fail, and
* the mutex' lock count will not be incremented.<br>
* Can be called only with kernel paused one level deep.
* (pauseKernel calls can be nested).
* \param dLock the PauseKernelLock instance that paused the kernel.
* \return true if the lock was acquired
*/
bool PKtryLock(PauseKernelLock& dLock);
/**
* Unlock mutex, can be called only with kernel paused one level deep
* (pauseKernel calls can be nested).<br>
* \param dLock the PauseKernelLock instance that paused the kernel.
* \return true if a higher priority thread was woken
*/
bool PKunlock(PauseKernelLock& dLock);
/**
* Unlock mutex all levels of a recursive mutex, can be called only with
* kernel paused one level deep (pauseKernel calls can be nested).<br>
* \param dLock the PauseKernelLock instance that paused the kernel.
* \return the mutex recursive depth (how many times it was locked by the
* owner). Zero means the mutex is locked one level deep (lock() was called
* once), one means two levels deep, etc.
*/
unsigned int PKunlockAllDepthLevels(PauseKernelLock& dLock);
/// Thread currently inside critical section, if NULL the critical section
/// is free
Thread *owner;
/// If this mutex is locked, it is added to a list of mutexes held by the
/// thread that owns this mutex. This field is necessary to make the list.
Mutex *next;
/// Waiting thread are stored in this min-heap, sorted by priority
std::vector<Thread *> waiting;
/// Used to hold nesting depth for recursive mutexes, -1 if not recursive
int recursiveDepth;
//Friends
friend class ConditionVariable;
friend class Thread;
};
/**
* Very simple RAII style class to lock a mutex in an exception-safe way.
* Mutex is acquired by the constructor and released by the destructor.
*/
template<typename T>
class Lock
{
public:
/**
* Constructor: locks the mutex
* \param m mutex to lock
*/
explicit Lock(T& m): mutex(m)
{
mutex.lock();
}
/**
* Destructor: unlocks the mutex
*/
~Lock()
{
mutex.unlock();
}
/**
* \return the locked mutex
*/
T& get()
{
return mutex;
}
private:
//Unwanted methods
Lock(const Lock& l);///< No public copy constructor
Lock& operator = (const Lock& l);///< No publc operator =
T& mutex;///< Reference to locked mutex
};
/**
* This class allows to temporarily re unlock a mutex in a scope where
* it is locked <br>
* Example:
* \code
* Mutex m;
*
* //Mutex unlocked
* {
* Lock<Mutex> dLock(m);
*
* //Now mutex locked
*
* {
* Unlock<Mutex> eLock(dLock);
*
* //Now mutex back unlocked
* }
*
* //Now mutex again locked
* }
* //Finally mutex unlocked
* \endcode
*/
template<typename T>
class Unlock
{
public:
/**
* Constructor, unlock mutex.
* \param l the Lock that locked the mutex.
*/
explicit Unlock(Lock<T>& l): mutex(l.get())
{
mutex.unlock();
}
/**
* Constructor, unlock mutex.
* \param m a locked mutex.
*/
Unlock(T& m): mutex(m)
{
mutex.unlock();
}
/**
* Destructor.
* Disable back interrupts.
*/
~Unlock()
{
mutex.lock();
}
/**
* \return the unlocked mutex
*/
T& get()
{
return mutex;
}
private:
//Unwanted methods
Unlock(const Unlock& l);
Unlock& operator= (const Unlock& l);
T& mutex;///< Reference to locked mutex
};
/**
* A condition variable class for thread synchronization, available from
* Miosix 1.53.<br>
* One or more threads can wait on the condition variable, and the signal()
* and broadcast() allow to wake ne or all the waiting threads.<br>
* This class is meant to be a static or global class. Dynamically creating a
* ConditionVariable with new or on the stack must be done with care, to avoid
* deleting a ConditionVariable while some threads are waiting, and to avoid
* situations where a thread tries to wait on a deleted ConditionVariable.<br>
*/
class ConditionVariable
{
public:
/**
* Constructor, initializes the ConditionVariable.
*/
ConditionVariable();
/**
* Unlock the mutex and wait.
* If more threads call wait() they must do so specifying the same mutex,
* otherwise the behaviour is undefined.
* \param l A Lock instance that locked a Mutex
*/
template<typename T>
void wait(Lock<T>& l)
{
wait(l.get());
}
/**
* Unlock the Mutex and wait.
* If more threads call wait() they must do so specifying the same mutex,
* otherwise the behaviour is undefined.
* \param m a locked Mutex
*/
void wait(Mutex& m);
/**
* Unlock the FastMutex and wait.
* If more threads call wait() they must do so specifying the same mutex,
* otherwise the behaviour is undefined.
* \param m a locked Mutex
*/
void wait(FastMutex& m);
/**
* Wakeup one waiting thread.
* Currently implemented policy is fifo.
*/
void signal();
/**
* Wakeup all waiting threads.
*/
void broadcast();
private:
//Unwanted methods
ConditionVariable(const ConditionVariable& );
ConditionVariable& operator= (const ConditionVariable& );
/**
* \internal
* \struct WaitingData
* This struct is used to make a list of waiting threads.
*/
struct WaitingData
{
Thread *p;///<\internal Thread that is waiting
WaitingData *next;///<\internal Next thread in the list
};
WaitingData *first;///<Pointer to first element of waiting fifo
WaitingData *last;///<Pointer to last element of waiting fifo
};
/**
* A timer that can be used to measure time intervals.<br>Its resolution equals
* the kernel tick.<br>Maximum interval is 2^31-1 ticks.
*/
class Timer
{
public:
/**
* Constructor. Timer is initialized in stopped status.
*/
Timer();
/**
* Start the timer
*/
void start();
/**
* Stop the timer. After stop, Timer can be started and stopped again to
* count non-contiguous timer intervals.
*/
void stop();
/**
* \return true if timer is running
*/
bool isRunning() const;
/**
* get the interval, in kernel ticks.
* \return the number of tick between start and stop. Returns -1 if the
* timer was never started, if interval is called after start but before
* stop, or if it overflowed.
*
* To read the vaue of a timer without stopping it, you can use its copy
* constructor to create another timer, and stop it while the first timer
* keeps running.
*/
int interval() const;
/**
* Clear the timer and set it to not running state.
*/
void clear();
//Using default copy constructor, operator = and destructor
private:
//Timer data
bool first;///< True if start has never been called
bool running;///< True if timer is running
long long start_tick;///< The kernel tick when start was called.
long long tick_count;///< The tick count
};
/**
* \}
*/
} //namespace miosix
#endif //SYNC_H