Dependencies:   mbed

scmRTOS/Common/OS_Services.cpp

Committer:
mbed714
Date:
2010-09-21
Revision:
0:331db0b44b67

File content as of revision 0:331db0b44b67:

//******************************************************************************
//*
//*     FULLNAME:  Single-Chip Microcontroller Real-Time Operating System
//*
//*     NICKNAME:  scmRTOS
//*
//*     PURPOSE:  OS Services Source
//*
//*     Version: 3.10
//*
//*     $Revision: 256 $
//*     $Date:: 2010-01-22 #$
//*
//*     Copyright (c) 2003-2010, Harry E. Zhurov
//*
//*     Permission is hereby granted, free of charge, to any person
//*     obtaining  a copy of this software and associated documentation
//*     files (the "Software"), to deal in the Software without restriction,
//*     including without limitation the rights to use, copy, modify, merge,
//*     publish, distribute, sublicense, and/or sell copies of the Software,
//*     and to permit persons to whom the Software is furnished to do so,
//*     subject to the following conditions:
//*
//*     The above copyright notice and this permission notice shall be included
//*     in all copies or substantial portions of the Software.
//*
//*     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//*     EXPRESS  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//*     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//*     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
//*     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
//*     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
//*     THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//*
//*     =================================================================
//*     See http://scmrtos.sourceforge.net for documentation, latest
//*     information, license and contact details.
//*     =================================================================
//*
//******************************************************************************

#include "scmRTOS.h"

using namespace OS;

//------------------------------------------------------------------------------
//
//
//      TEventFlag
//
//
bool OS::TEventFlag::Wait(TTimeout timeout)
{
    TCritSect cs;

    if(Value)                                           // if flag already signaled
    {
        Value = efOff;                                  // clear flag
        return true;
    }
    else
    {
        TBaseProcess* p = Kernel.ProcessTable[Kernel.CurProcPriority];
        p->Timeout = timeout;
        TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);

        SetPrioTag(ProcessMap, PrioTag);                // put current process to the wait map
        ClrPrioTag(Kernel.ReadyProcessMap, PrioTag);    // remove current process from the ready map

        Kernel.Scheduler();

        p->Timeout = 0;

        if( !(ProcessMap & PrioTag) )                   // if waked up by signal() or signal_ISR()
            return true;

        ClrPrioTag(ProcessMap, PrioTag);                // otherwise waked up by timeout or by
        return false;                                   // OS::ForceWakeUpProcess(), remove process from the wait map
    }
}
//------------------------------------------------------------------------------
void OS::TEventFlag::Signal()
{
    TCritSect cs;
    if(ProcessMap)                                          // if any process waits for event
    {
        TProcessMap Timeouted = Kernel.ReadyProcessMap;     // Process has its tag set in ReadyProcessMap if timeout expired
                                                            // or it was waked up by OS::ForceWakeUpProcess()

        if( ProcessMap & ~Timeouted )                       // if any process has to be waked up
        {
            SetPrioTag(Kernel.ReadyProcessMap, ProcessMap); // place all waiting processes to the ready map
            ClrPrioTag(ProcessMap, ~Timeouted);             // remove all non-timeouted processes from the waiting map.
                                                            // Used to check that process waked up by signal() or signalISR()
                                                            // and not by timeout or OS::ForceWakeUpProcess()
            Kernel.Scheduler();
            return;
        }
    }
    Value = efOn;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//
//
//      TMutex
//
//
void OS::TMutex::Lock()
{
    TCritSect cs;

    TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
    while(ValueTag)
    {
        SetPrioTag(ProcessMap, PrioTag);             // mutex already locked by another process, put current process to the wait map
        ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map

        Kernel.Scheduler();
    }
    ValueTag = PrioTag;                              // mutex has been successfully locked
}
//------------------------------------------------------------------------------
void OS::TMutex::Unlock()
{
    TCritSect cs;

    TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
    if(ValueTag != PrioTag) return;                  // the only process that had locked mutex can unlock the mutex
    ValueTag = 0;
    if(ProcessMap)
    {
        byte pr = GetHighPriority(ProcessMap);
        PrioTag = GetPrioTag(pr);
        ClrPrioTag(ProcessMap, PrioTag);             // remove next ready process from the wait map
        SetPrioTag(Kernel.ReadyProcessMap, PrioTag); // place next process to the ready map
        Kernel.Scheduler();
    }
}
//------------------------------------------------------------------------------
void OS::TMutex::UnlockISR()
{
    TCritSect cs;

    ValueTag = 0;
    if(ProcessMap)
    {
        byte pr = GetHighPriority(ProcessMap);
        TProcessMap PrioTag = GetPrioTag(pr);
        ClrPrioTag(ProcessMap, PrioTag);             // remove next ready process from the wait map
        SetPrioTag(Kernel.ReadyProcessMap, PrioTag); // place next process to the ready map
    }
}
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
//
//
//      TChannel
//
//
void OS::TChannel::CheckWaiters(TProcessMap& pm)
{
    if(pm)
    {
        byte pr = GetHighPriority(pm);
        TProcessMap PrioTag = GetPrioTag(pr);
        ClrPrioTag(pm, PrioTag);                     // remove next ready process from the wait map
        SetPrioTag(Kernel.ReadyProcessMap, PrioTag); // place next process to the ready map
        Kernel.Scheduler();
    }
}
//------------------------------------------------------------------------------
void OS::TChannel::Push(byte x)
{
    TCritSect cs;

    while (!Cbuf.get_free_size())
    {
        TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
        SetPrioTag  (ProducersProcessMap, PrioTag);  // channel is full, put current process to the wait map
        ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
        Kernel.Scheduler();                          // wait until waked-up by Pop() or Read()
    }

    Cbuf.put(x);
    CheckWaiters(ConsumersProcessMap);
}
//------------------------------------------------------------------------------
byte OS::TChannel::Pop()
{
    TCritSect cs;
    byte x;

    while(!Cbuf.get_count())
    {
        TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
        SetPrioTag(ConsumersProcessMap, PrioTag);    // channel is empty, put current process to the wait map
        ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
        Kernel.Scheduler();                          // wait until waked up by Push() or Write()
    }
    x = Cbuf.get();
    CheckWaiters(ProducersProcessMap);
    return x;
}
//------------------------------------------------------------------------------
void OS::TChannel::Write(const byte* data, const byte count)
{
    TCritSect cs;

    while(Cbuf.get_free_size() < count)
    {
        TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
        SetPrioTag(ProducersProcessMap, PrioTag);    // channel has not enough space, put current process to the wait map
        ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
        Kernel.Scheduler();                          // wait until waked up by Read() or Pop()
    }

    Cbuf.write(data, count);
    CheckWaiters(ConsumersProcessMap);
}
//------------------------------------------------------------------------------
void OS::TChannel::Read(byte* const data, const byte count)
{
    TCritSect cs;

    while(Cbuf.get_count() < count)
    {
        TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
        SetPrioTag(ConsumersProcessMap, PrioTag);    // channel doesn't contain enough data, put current process to the wait map
        ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
        Kernel.Scheduler();                          // wait until waked up by Write() or Push()
    }

    Cbuf.read(data, count);
    CheckWaiters(ProducersProcessMap);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//
//              OS::message template
//
//          Function-members implementation
//
//
//------------------------------------------------------------------------------
bool OS::TBaseMessage::wait(TTimeout timeout)
{
    TCritSect cs;

    if(NonEmpty)                                                  // message alredy send
    {
        NonEmpty = false;
        return true;
    }
    else
    {
        TBaseProcess* p = Kernel.ProcessTable[Kernel.CurProcPriority];
        p->Timeout = timeout;
        TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);

        SetPrioTag(ProcessMap, PrioTag);                          // put current process to the wait map
        ClrPrioTag(Kernel.ReadyProcessMap, PrioTag);              // remove current process from the ready map
        Kernel.Scheduler();                                       // wait until wake up

        p->Timeout = 0;
        if( !(ProcessMap & PrioTag) )                             // if waked up by send() or sendISR()
            return true;

        ClrPrioTag(ProcessMap, PrioTag);                          // otherwise waked up by timeout or by
            return false;                                         // OS::ForceWakeUpProcess(), remove process from wait map
    }
}
//------------------------------------------------------------------------------
void OS::TBaseMessage::send()
{
    TCritSect cs;

    if(ProcessMap)
    {
        TProcessMap Timeouted = Kernel.ReadyProcessMap;         // Process has its tag set in ReadyProcessMap if timeout expired,
                                                                // or it was waked up by OS::ForceWakeUpProcess()
        if( ProcessMap & ~Timeouted )                           // if any process has to be waked up
        {
            SetPrioTag(Kernel.ReadyProcessMap, ProcessMap);     // place all waiting processes to the ready map
            ClrPrioTag(ProcessMap, ~Timeouted);                 // remove all non-timeouted processes from the waiting map.
            Kernel.Scheduler();
            return;
        }
    }

    NonEmpty = true;
}
//------------------------------------------------------------------------------