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

517 wiersze
15 KiB
C++

/***************************************************************************
* Copyright (C) 2008, 2009, 2010, 2011 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
#include "sync.h"
#include "kernel.h"
#include "kernel/scheduler/scheduler.h"
#include "error.h"
#include "pthread_private.h"
#include <algorithm>
using namespace std;
namespace miosix {
//
// class Mutex
//
Mutex::Mutex(Options opt): owner(0), next(0), waiting()
{
recursiveDepth= opt==RECURSIVE ? 0 : -1;
}
void Mutex::PKlock(PauseKernelLock& dLock)
{
Thread *p=Thread::getCurrentThread();
if(owner==0)
{
owner=p;
//Save original thread priority, if the thread has not yet locked
//another mutex
if(owner->mutexLocked==0) owner->savedPriority=owner->getPriority();
//Add this mutex to the list of mutexes locked by owner
this->next=owner->mutexLocked;
owner->mutexLocked=this;
return;
}
//This check is very important. Without this attempting to lock the same
//mutex twice won't cause a deadlock because the Thread::IRQwait() is
//enclosed in a while(owner!=p) which is immeditely false.
if(owner==p)
{
if(recursiveDepth>=0)
{
recursiveDepth++;
return;
} else errorHandler(MUTEX_DEADLOCK); //Bad, deadlock
}
//Add thread to mutex' waiting queue
waiting.push_back(p);
LowerPriority l;
push_heap(waiting.begin(),waiting.end(),l);
//Handle priority inheritance
if(p->mutexWaiting!=0) errorHandler(UNEXPECTED);
p->mutexWaiting=this;
if(p->getPriority()>owner->getPriority())
{
Thread *walk=owner;
for(;;)
{
Scheduler::PKsetPriority(walk,p->getPriority());
if(walk->mutexWaiting==0) break;
make_heap(walk->mutexWaiting->waiting.begin(),
walk->mutexWaiting->waiting.end(),l);
walk=walk->mutexWaiting->owner;
}
}
//The while is necessary because some other thread might call wakeup()
//on this thread. So the thread can wakeup also for other reasons not
//related to the mutex becoming free
while(owner!=p)
{
//Wait can only be called with kernel started, while IRQwait can
//only be called with interupts disabled, so that's why interrupts
//are disabled
{
FastInterruptDisableLock l;
Thread::IRQwait();//Return immediately
}
{
RestartKernelLock eLock(dLock);
//Now the IRQwait becomes effective
Thread::yield();
}
}
}
void Mutex::PKlockToDepth(PauseKernelLock& dLock, unsigned int depth)
{
Thread *p=Thread::getCurrentThread();
if(owner==0)
{
owner=p;
if(recursiveDepth>=0) recursiveDepth=depth;
//Save original thread priority, if the thread has not yet locked
//another mutex
if(owner->mutexLocked==0) owner->savedPriority=owner->getPriority();
//Add this mutex to the list of mutexes locked by owner
this->next=owner->mutexLocked;
owner->mutexLocked=this;
return;
}
//This check is very important. Without this attempting to lock the same
//mutex twice won't cause a deadlock because the Thread::IRQwait() is
//enclosed in a while(owner!=p) which is immeditely false.
if(owner==p)
{
if(recursiveDepth>=0)
{
recursiveDepth=depth;
return;
} else errorHandler(MUTEX_DEADLOCK); //Bad, deadlock
}
//Add thread to mutex' waiting queue
waiting.push_back(p);
LowerPriority l;
push_heap(waiting.begin(),waiting.end(),l);
//Handle priority inheritance
if(p->mutexWaiting!=0) errorHandler(UNEXPECTED);
p->mutexWaiting=this;
if(p->getPriority()>owner->getPriority())
{
Thread *walk=owner;
for(;;)
{
Scheduler::PKsetPriority(walk,p->getPriority());
if(walk->mutexWaiting==0) break;
make_heap(walk->mutexWaiting->waiting.begin(),
walk->mutexWaiting->waiting.end(),l);
walk=walk->mutexWaiting->owner;
}
}
//The while is necessary because some other thread might call wakeup()
//on this thread. So the thread can wakeup also for other reasons not
//related to the mutex becoming free
while(owner!=p)
{
//Wait can only be called with kernel started, while IRQwait can
//only be called with interupts disabled, so that's why interrupts
//are disabled
{
FastInterruptDisableLock l;
Thread::IRQwait();//Return immediately
}
{
RestartKernelLock eLock(dLock);
//Now the IRQwait becomes effective
Thread::yield();
}
}
if(recursiveDepth>=0) recursiveDepth=depth;
}
bool Mutex::PKtryLock(PauseKernelLock& dLock)
{
(void) dLock;
Thread *p=Thread::getCurrentThread();
if(owner==0)
{
owner=p;
//Save original thread priority, if the thread has not yet locked
//another mutex
if(owner->mutexLocked==0) owner->savedPriority=owner->getPriority();
//Add this mutex to the list of mutexes locked by owner
this->next=owner->mutexLocked;
owner->mutexLocked=this;
return true;
}
if(owner==p && recursiveDepth>=0)
{
recursiveDepth++;
return true;
}
return false;
}
bool Mutex::PKunlock(PauseKernelLock& dLock)
{
(void) dLock;
Thread *p=Thread::getCurrentThread();
if(owner!=p) return false;
if(recursiveDepth>0)
{
recursiveDepth--;
return false;
}
//Remove this mutex from the list of mutexes locked by the owner
if(owner->mutexLocked==this)
{
owner->mutexLocked=owner->mutexLocked->next;
} else {
Mutex *walk=owner->mutexLocked;
for(;;)
{
//this Mutex not in owner's list? impossible
if(walk->next==0) errorHandler(UNEXPECTED);
if(walk->next==this)
{
walk->next=walk->next->next;
break;
}
walk=walk->next;
}
}
//Handle priority inheritance
if(owner->mutexLocked==0)
{
//Not locking any other mutex
if(owner->savedPriority!=owner->getPriority())
Scheduler::PKsetPriority(owner,owner->savedPriority);
} else {
Priority pr=owner->savedPriority;
//Calculate new priority of thread, which is
//max(savedPriority, inheritedPriority)
Mutex *walk=owner->mutexLocked;
while(walk!=0)
{
if(walk->waiting.empty()==false)
pr=max(pr,walk->waiting.front()->getPriority());
walk=walk->next;
}
if(pr!=owner->getPriority()) Scheduler::PKsetPriority(owner,pr);
}
//Choose next thread to lock the mutex
if(waiting.empty()==false)
{
//There is at least another thread waiting
owner=waiting.front();
LowerPriority l;
pop_heap(waiting.begin(),waiting.end(),l);
waiting.pop_back();
if(owner->mutexWaiting!=this) errorHandler(UNEXPECTED);
owner->mutexWaiting=0;
owner->PKwakeup();
if(owner->mutexLocked==0) owner->savedPriority=owner->getPriority();
//Add this mutex to the list of mutexes locked by owner
this->next=owner->mutexLocked;
owner->mutexLocked=this;
//Handle priority inheritance of new owner
if(waiting.empty()==false &&
waiting.front()->getPriority()>owner->getPriority())
Scheduler::PKsetPriority(owner,waiting.front()->getPriority());
return owner->getPriority() > p->getPriority();
} else {
owner=0; //No threads waiting
std::vector<Thread *>().swap(waiting); //Save some RAM
return false;
}
}
unsigned int Mutex::PKunlockAllDepthLevels(PauseKernelLock& dLock)
{
(void) dLock;
Thread *p=Thread::getCurrentThread();
if(owner!=p) return 0;
//Remove this mutex from the list of mutexes locked by the owner
if(owner->mutexLocked==this)
{
owner->mutexLocked=owner->mutexLocked->next;
} else {
Mutex *walk=owner->mutexLocked;
for(;;)
{
//this Mutex not in owner's list? impossible
if(walk->next==0) errorHandler(UNEXPECTED);
if(walk->next==this)
{
walk->next=walk->next->next;
break;
}
walk=walk->next;
}
}
//Handle priority inheritance
if(owner->mutexLocked==0)
{
//Not locking any other mutex
if(owner->savedPriority!=owner->getPriority())
Scheduler::PKsetPriority(owner,owner->savedPriority);
} else {
Priority pr=owner->savedPriority;
//Calculate new priority of thread, which is
//max(savedPriority, inheritedPriority)
Mutex *walk=owner->mutexLocked;
while(walk!=0)
{
if(walk->waiting.empty()==false)
pr=max(pr,walk->waiting.front()->getPriority());
walk=walk->next;
}
if(pr!=owner->getPriority()) Scheduler::PKsetPriority(owner,pr);
}
//Choose next thread to lock the mutex
if(waiting.empty()==false)
{
//There is at least another thread waiting
owner=waiting.front();
LowerPriority l;
pop_heap(waiting.begin(),waiting.end(),l);
waiting.pop_back();
if(owner->mutexWaiting!=this) errorHandler(UNEXPECTED);
owner->mutexWaiting=0;
owner->PKwakeup();
if(owner->mutexLocked==0) owner->savedPriority=owner->getPriority();
//Add this mutex to the list of mutexes locked by owner
this->next=owner->mutexLocked;
owner->mutexLocked=this;
//Handle priority inheritance of new owner
if(waiting.empty()==false &&
waiting.front()->getPriority()>owner->getPriority())
Scheduler::PKsetPriority(owner,waiting.front()->getPriority());
} else {
owner=0; //No threads waiting
std::vector<Thread *>().swap(waiting); //Save some RAM
}
if(recursiveDepth<0) return 0;
unsigned int result=recursiveDepth;
recursiveDepth=0;
return result;
}
//
// class ConditionVariable
//
ConditionVariable::ConditionVariable(): first(0), last(0) {}
void ConditionVariable::wait(Mutex& m)
{
PauseKernelLock dLock;
WaitingData w;
w.p=Thread::getCurrentThread();
w.next=0;
//Add entry to tail of list
if(first==0)
{
first=last=&w;
} else {
last->next=&w;
last=&w;
}
//Unlock mutex and wait
{
FastInterruptDisableLock l;
w.p->flags.IRQsetCondWait(true);
}
unsigned int depth=m.PKunlockAllDepthLevels(dLock);
{
RestartKernelLock eLock(dLock);
Thread::yield(); //Here the wait becomes effective
}
m.PKlockToDepth(dLock,depth);
}
void ConditionVariable::wait(FastMutex& m)
{
FastInterruptDisableLock dLock;
WaitingData w;
w.p=Thread::getCurrentThread();
w.next=0;
//Add entry to tail of list
if(first==0)
{
first=last=&w;
} else {
last->next=&w;
last=&w;
}
//Unlock mutex and wait
w.p->flags.IRQsetCondWait(true);
unsigned int depth=IRQdoMutexUnlockAllDepthLevels(m.get());
{
FastInterruptEnableLock eLock(dLock);
Thread::yield(); //Here the wait becomes effective
}
IRQdoMutexLockToDepth(m.get(),dLock,depth);
}
void ConditionVariable::signal()
{
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(first==0) return;
//Wakeup
first->p->flags.IRQsetCondWait(false);
//Check for priority issues
if(first->p->IRQgetPriority() >
Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true;
//Remove from list
first=first->next;
}
//If the woken thread has higher priority than our priority, yield
if(hppw) Thread::yield();
}
void ConditionVariable::broadcast()
{
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;
while(first!=0)
{
//Wakeup
first->p->flags.IRQsetCondWait(false);
//Check for priority issues
if(first->p->IRQgetPriority() >
Thread::IRQgetCurrentThread()->IRQgetPriority()) hppw=true;
//Remove from list
first=first->next;
}
}
//If at least one of the woken thread has higher priority than our priority,
//yield
if(hppw) Thread::yield();
}
//
// class Timer
//
Timer::Timer()
{
first=true;
running=false;
start_tick=tick_count=0;
}
void Timer::start()
{
first=false;
running=true;
start_tick=getTick();
}
void Timer::stop()
{
if(running==false) return;
running=false;
tick_count+=getTick()-start_tick;
start_tick=0;
}
bool Timer::isRunning() const
{
return running;
}
int Timer::interval() const
{
if((first==true)||(running==true)||(tick_count>2147483647)) return -1;
return (int)tick_count;
}
void Timer::clear()
{
first=true;
running=false;
start_tick=tick_count=0;
}
} //namespace miosix