/***************************************************************************
 *   Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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 "kernel.h"
#include "interfaces/portability.h"
#include "interfaces/atomic_ops.h"
#include "error.h"
#include "logging.h"
#include "arch_settings.h"
#include "sync.h"
#include "stage_2_boot.h"
#include "process.h"
#include "kernel/scheduler/scheduler.h"
#include "stdlib_integration/libc_integration.h"
#include <stdexcept>
#include <algorithm>
#include <string.h>
#include <reent.h>

/*
Used by assembler context switch macros
This variable is set by miosix::IRQfindNextThread in file kernel.cpp
*/
extern "C" {
volatile unsigned int *ctxsave;
}


namespace miosix {

//Global variables used by kernel.cpp. Those that are not static are also used
//in portability.cpp and by the schedulers.
//These variables MUST NOT be used outside kernel.cpp and portability.cpp

volatile Thread *cur=NULL;///<\internal Thread currently running

///\internal True if there are threads in the DELETED status. Used by idle thread
static volatile bool exist_deleted=false;

static SleepData *sleeping_list=NULL;///<\internal list of sleeping threads

static volatile long long tick=0;///<\internal Kernel tick

///\internal !=0 after pauseKernel(), ==0 after restartKernel()
volatile int kernel_running=0;

///\internal true if a tick occurs while the kernel is paused
volatile bool tick_skew=false;

static bool kernel_started=false;///<\internal becomes true after startKernel.

/// This is used by disableInterrupts() and enableInterrupts() to allow nested
/// calls to these functions.
static unsigned char interruptDisableNesting=0;

#ifdef WITH_PROCESSES

/// The proc field of the Thread class for kernel threads points to this object
static ProcessBase *kernel=nullptr;

#endif //WITH_PROCESSES

/**
 * \internal
 * Idle thread. Created when the kernel is started, it phisically deallocates
 * memory for deleted threads, and puts the cpu in sleep mode.
 */
void *idleThread(void *argv)
{
    (void) argv;

    for(;;)
    {
        if(exist_deleted)
        {
            PauseKernelLock lock;
            exist_deleted=false;
            Scheduler::PKremoveDeadThreads();
        }
        #ifndef JTAG_DISABLE_SLEEP
        //JTAG debuggers lose communication with the device if it enters sleep
        //mode, so to use debugging it is necessary to remove this instruction
        miosix_private::sleepCpu();
        #endif
    }
    return 0; //Just to avoid a compiler warning
}

void disableInterrupts()
{
    //Before the kernel is started interrupts are disabled,
    //so disabling them again won't hurt
    miosix_private::doDisableInterrupts();
    if(interruptDisableNesting==0xff) errorHandler(NESTING_OVERFLOW);
    interruptDisableNesting++;
}

void enableInterrupts()
{
    if(interruptDisableNesting==0)
    {
        //Bad, enableInterrupts was called one time more than disableInterrupts
        errorHandler(DISABLE_INTERRUPTS_NESTING);
    }
    interruptDisableNesting--;
    if(interruptDisableNesting==0 && kernel_started==true)
    {
        miosix_private::doEnableInterrupts();
    }
}

void pauseKernel()
{
    int old=atomicAddExchange(&kernel_running,1);
    if(old>=0xff) errorHandler(NESTING_OVERFLOW);
}

void restartKernel()
{
    int old=atomicAddExchange(&kernel_running,-1);
    if(old<=0) errorHandler(PAUSE_KERNEL_NESTING);
    
    //Check interruptDisableNesting to allow pauseKernel() while interrupts
    //are disabled with an InterruptDisableLock
    if(interruptDisableNesting==0)
    {
        //If we missed some tick yield immediately
        if(old==1 && tick_skew)
        { 
            tick_skew=false;
            Thread::yield();
        }
    }
}

bool areInterruptsEnabled()
{
    return miosix_private::checkAreInterruptsEnabled();
}

void startKernel()
{
    #ifdef WITH_PROCESSES
    try {
        kernel=new ProcessBase;
    } catch(...) {
        errorHandler(OUT_OF_MEMORY);
    }
    #endif //WITH_PROCESSES
    
    // As a side effect this function allocates the idle thread and makes cur
    // point to it. It's probably been called many times during boot by the time
    // we get here, but we can't be sure
    auto *idle=Thread::IRQgetCurrentThread();
    
    #ifdef WITH_PROCESSES
    // If the idle thread was allocated before startKernel(), then its proc
    // is nullptr. We can't move kernel=new ProcessBase; earlier than this
    // function, though
    idle->proc=kernel;
    #endif //WITH_PROCESSES

    // Create the idle and main thread
    Thread *main;
    main=Thread::doCreate(mainLoader,MAIN_STACK_SIZE,nullptr,Thread::DEFAULT,true);
    if(main==nullptr) errorHandler(OUT_OF_MEMORY);
    
    // Add them to the scheduler
    if(Scheduler::PKaddThread(main,MAIN_PRIORITY)==false) errorHandler(UNEXPECTED);

    // Idle thread needs to be set after main (see control_scheduler.cpp)
    Scheduler::IRQsetIdleThread(idle);
    
    // Make the C standard library use per-thread reeentrancy structure
    setCReentrancyCallback(Thread::getCReent);
    
    // Now kernel is started
    kernel_started=true;
    
    // Dispatch the task to the architecture-specific function
    miosix_private::IRQportableStartKernel();
}

bool isKernelRunning()
{
    return (kernel_running==0) && kernel_started;
}

long long getTick()
{
    /*
     * Reading a volatile 64bit integer on a 32bit platform with interrupts
     * enabled is tricky because the operation is not atomic, so we need to
     * read it twice to see if we were interrupted in the middle of the read
     * operation.
     */
    long long a,b;
    for(;;)
    {
        a=static_cast<long long>(tick);
        b=static_cast<long long>(tick);
        if(a==b) return a;
    }
}

/**
 * \internal
 * Used by Thread::sleep() to add a thread to sleeping list. The list is sorted
 * by the wakeup_time field to reduce time required to wake threads during
 * context switch.
 * Also sets thread SLEEP_FLAG. It is labeled IRQ not because it is meant to be
 * used inside an IRQ, but because interrupts must be disabled prior to calling
 * this function.
 */
void IRQaddToSleepingList(SleepData *x)
{
    x->p->flags.IRQsetSleep(true);
    if((sleeping_list==NULL)||(x->wakeup_time <= sleeping_list->wakeup_time))
    {
        x->next=sleeping_list;
        sleeping_list=x;   
    } else {
       SleepData *cur=sleeping_list;
       for(;;)
       {
           if((cur->next==NULL)||(x->wakeup_time <= cur->next->wakeup_time))
           {
               x->next=cur->next;
               cur->next=x;
               break;
           }
           cur=cur->next;
       }
    }
}

/**
 * \internal
 * Called @ every tick to check if it's time to wake some thread.
 * Also increases the system tick.
 * Takes care of clearing SLEEP_FLAG.
 * It is used by the kernel, and should not be used by end users.
 * \return true if some thread was woken.
 */
bool IRQwakeThreads()
{
    tick++;//Increment tick
    bool result=false;
    for(;;)
    {
        if(sleeping_list==NULL) break;//If no item in list, return
        //Since list is sorted, if we don't need to wake the first element
        //we don't need to wake the other too
        if(tick != sleeping_list->wakeup_time) break;
        sleeping_list->p->flags.IRQsetSleep(false);//Wake thread
        sleeping_list=sleeping_list->next;//Remove from list
        result=true;
    }
    return result;
}

/*
Memory layout for a thread
	|------------------------|
	|     class Thread       |
	|------------------------|<-- this
	|         stack          |
	|           |            |
	|           V            |
	|------------------------|
	|       watermark        |
	|------------------------|<-- base, watermark
*/

Thread *Thread::create(void *(*startfunc)(void *), unsigned int stacksize,
					Priority priority, void *argv, unsigned short options)
{
    //Check to see if input parameters are valid
    if(priority.validate()==false || stacksize<STACK_MIN) return NULL;
    
    Thread *thread=doCreate(startfunc,stacksize,argv,options,false);
    if(thread==NULL) return NULL;
    
    //Add thread to thread list
    {
        //Handling the list of threads, critical section is required
        PauseKernelLock lock;
        if(Scheduler::PKaddThread(thread,priority)==false)
        {
            //Reached limit on number of threads
            unsigned int *base=thread->watermark;
            thread->~Thread();
            free(base); //Delete ALL thread memory
            return NULL;
        }
    }
    #ifdef SCHED_TYPE_EDF
    if(isKernelRunning()) yield(); //The new thread might have a closer deadline
    #endif //SCHED_TYPE_EDF
    return thread;
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
Thread *Thread::create(void (*startfunc)(void *), unsigned int stacksize,
					Priority priority, void *argv, unsigned short options)
{
    //Just call the other version with a cast.
    return Thread::create(reinterpret_cast<void *(*)(void*)>(startfunc),
            stacksize,priority,argv,options);
}
#pragma GCC diagnostic pop

void Thread::yield()
{
    miosix_private::doYield();
}

bool Thread::testTerminate()
{
    //Just reading, no need for critical section
    return const_cast<Thread*>(cur)->flags.isDeleting();
}
	
void Thread::sleep(unsigned int ms)
{
    if(ms==0) return;
    //The SleepData variable has to be in scope till Thread::yield() returns
    //as IRQaddToSleepingList() makes it part of a linked list till the
    //thread wakes up (i.e: after Thread::yield() returns)
    SleepData d;
    //pauseKernel() here is not enough since even if the kernel is stopped
    //the tick isr will wake threads, modifying the sleeping_list
    {
        FastInterruptDisableLock lock;
        d.p=const_cast<Thread*>(cur);
        if(((ms*TICK_FREQ)/1000)>0) d.wakeup_time=getTick()+(ms*TICK_FREQ)/1000;
        //If tick resolution is too low, wait one tick
        else d.wakeup_time=getTick()+1;
        IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG
    }
    Thread::yield();
}

void Thread::sleepUntil(long long absoluteTime)
{
    //The SleepData variable has to be in scope till Thread::yield() returns
    //as IRQaddToSleepingList() makes it part of a linked list till the
    //thread wakes up (i.e: after Thread::yield() returns)
    SleepData d;
    //pauseKernel() here is not enough since even if the kernel is stopped
    //the tick isr will wake threads, modifying the sleeping_list
    {
        FastInterruptDisableLock lock;
        if(absoluteTime<=getTick()) return; //Wakeup time in the past, return
        d.p=const_cast<Thread*>(cur);
        d.wakeup_time=absoluteTime;
        IRQaddToSleepingList(&d);//Also sets SLEEP_FLAG
    }
    Thread::yield();
}

Thread *Thread::getCurrentThread()
{
    Thread *result=const_cast<Thread*>(cur);
    if(result) return result;
    //This function must always return a pointer to a valid thread. The first
    //time this is called before the kernel is started, however, cur is nullptr.
    //thus we allocate the idle thread and return a pointer to that.
    return allocateIdleThread();
}

bool Thread::exists(Thread *p)
{
    if(p==NULL) return false;
    PauseKernelLock lock;
    return Scheduler::PKexists(p);
}

Priority Thread::getPriority()
{
    return Scheduler::getPriority(this);
}

void Thread::setPriority(Priority pr)
{
    if(pr.validate()==false) return;
    PauseKernelLock lock;

    Thread *current=getCurrentThread();
    //If thread is locking at least one mutex
    if(current->mutexLocked!=0)
    {   
        //savedPriority always changes, since when all mutexes are unlocked
        //setPriority() must become effective
        if(current->savedPriority==pr) return;
        current->savedPriority=pr;
        //Calculate new priority of thread, which is
        //max(savedPriority, inheritedPriority)
        Mutex *walk=current->mutexLocked;
        while(walk!=0)
        {
            if(walk->waiting.empty()==false)
                pr=std::max(pr,walk->waiting.front()->getPriority());
            walk=walk->next;
        }
    }
    
    //If old priority == desired priority, nothing to do.
    if(pr==current->getPriority()) return;
    Scheduler::PKsetPriority(current,pr);
    #ifdef SCHED_TYPE_EDF
    if(isKernelRunning()) yield(); //Another thread might have a closer deadline
    #endif //SCHED_TYPE_EDF
}

void Thread::terminate()
{
    //doing a read-modify-write operation on this->status, so pauseKernel is
    //not enough, we need to disable interrupts
    FastInterruptDisableLock lock;
    this->flags.IRQsetDeleting();
}

void Thread::wait()
{
    //pausing the kernel is not enough because of IRQwait and IRQwakeup
    {
        FastInterruptDisableLock lock;
        const_cast<Thread*>(cur)->flags.IRQsetWait(true);
    }
    Thread::yield();
    //Return here after wakeup
}
	
void Thread::wakeup()
{
    //pausing the kernel is not enough because of IRQwait and IRQwakeup
    {
        FastInterruptDisableLock lock;
        this->flags.IRQsetWait(false);
    }
    #ifdef SCHED_TYPE_EDF
    yield();//The other thread might have a closer deadline
    #endif //SCHED_TYPE_EDF
}

void Thread::PKwakeup()
{
    //pausing the kernel is not enough because of IRQwait and IRQwakeup
    FastInterruptDisableLock lock;
    this->flags.IRQsetWait(false);
}

void Thread::detach()
{
    FastInterruptDisableLock lock;
    this->flags.IRQsetDetached();
    
    //we detached a terminated thread, so its memory needs to be deallocated
    if(this->flags.isDeletedJoin()) exist_deleted=true;

    //Corner case: detaching a thread, but somebody else already called join
    //on it. This makes join return false instead of deadlocking
    if(this->joinData.waitingForJoin!=NULL)
    {
        //joinData is an union, so its content can be an invalid thread
        //this happens if detaching a thread that has already terminated
        if(this->flags.isDeletedJoin()==false)
        {
            //Wake thread, or it might sleep forever
            this->joinData.waitingForJoin->flags.IRQsetJoinWait(false);
        }
    }
}

bool Thread::isDetached() const
{
    return this->flags.isDetached();
}

bool Thread::join(void** result)
{
    {
        FastInterruptDisableLock dLock;
        if(this==Thread::IRQgetCurrentThread()) return false;
        if(Thread::IRQexists(this)==false) return false;
        if(this->flags.isDetached()) return false;
        if(this->flags.isDeletedJoin()==false)
        {
            //Another thread already called join on toJoin
            if(this->joinData.waitingForJoin!=NULL) return false;

            this->joinData.waitingForJoin=Thread::IRQgetCurrentThread();
            for(;;)
            {
                //Wait
                Thread::IRQgetCurrentThread()->flags.IRQsetJoinWait(true);
                {
                    FastInterruptEnableLock eLock(dLock);
                    Thread::yield();
                }
                if(Thread::IRQexists(this)==false) return false;
                if(this->flags.isDetached()) return false;
                if(this->flags.isDeletedJoin()) break;
            }
        }
        //Thread deleted, complete join procedure
        //Setting detached flag will make isDeleted() return true,
        //so its memory can be deallocated
        this->flags.IRQsetDetached();
        if(result!=NULL) *result=this->joinData.result;
    }
    {
        PauseKernelLock lock;
        //Since there is surely one dead thread, deallocate it immediately
        //to free its memory as soon as possible
        Scheduler::PKremoveDeadThreads();
    }
    return true;
}

Thread *Thread::IRQgetCurrentThread()
{
    //Implementation is the same as getCurrentThread, but to keep a consistent
    //interface this method is duplicated
    Thread *result=const_cast<Thread*>(cur);
    if(result) return result;
    //This function must always return a pointer to a valid thread. The first
    //time this is called before the kernel is started, however, cur is nullptr.
    //thus we allocate the idle thread and return a pointer to that.
    return allocateIdleThread();
}

Priority Thread::IRQgetPriority()
{
    //Implementation is the same as getPriority, but to keep a consistent
    //interface this method is duplicated
    return Scheduler::IRQgetPriority(this);
}

void Thread::IRQwait()
{
    const_cast<Thread*>(cur)->flags.IRQsetWait(true);
}

void Thread::IRQwakeup()
{
    this->flags.IRQsetWait(false);
}

bool Thread::IRQexists(Thread* p)
{
    if(p==NULL) return false;
    return Scheduler::PKexists(p);
}

const unsigned int *Thread::getStackBottom()
{
    return getCurrentThread()->watermark+(WATERMARK_LEN/sizeof(unsigned int));
}

int Thread::getStackSize()
{
    return getCurrentThread()->stacksize;
}

Thread *Thread::doCreate(void*(*startfunc)(void*) , unsigned int stacksize,
                      void* argv, unsigned short options, bool defaultReent)
{
    unsigned int fullStackSize=WATERMARK_LEN+CTXSAVE_ON_STACK+stacksize;
    
    //Align fullStackSize to the platform required stack alignment
    fullStackSize+=CTXSAVE_STACK_ALIGNMENT-1;
    fullStackSize/=CTXSAVE_STACK_ALIGNMENT;
    fullStackSize*=CTXSAVE_STACK_ALIGNMENT;
    
    //Allocate memory for the thread, return if fail
    unsigned int *base=static_cast<unsigned int*>(malloc(sizeof(Thread)+
            fullStackSize));
    if(base==NULL) return NULL;
    
    //At the top of thread memory allocate the Thread class with placement new
    void *threadClass=base+(fullStackSize/sizeof(unsigned int));
    Thread *thread=new (threadClass) Thread(base,stacksize,defaultReent);
    
    if(thread->cReentrancyData==nullptr)
    {
         thread->~Thread();
         free(base); //Delete ALL thread memory
         return NULL;
    }

    //Fill watermark and stack
    memset(base, WATERMARK_FILL, WATERMARK_LEN);
    base+=WATERMARK_LEN/sizeof(unsigned int);
    memset(base, STACK_FILL, fullStackSize-WATERMARK_LEN);
    
    //On some architectures some registers are saved on the stack, therefore
    //initCtxsave *must* be called after filling the stack.
    miosix_private::initCtxsave(thread->ctxsave,startfunc,
            reinterpret_cast<unsigned int*>(thread),argv);

    if((options & JOINABLE)==0) thread->flags.IRQsetDetached();
    return thread;
}

#ifdef WITH_PROCESSES

void Thread::IRQhandleSvc(unsigned int svcNumber)
{
    if(cur->proc==kernel) errorHandler(UNEXPECTED);
    if(svcNumber==SYS_USERSPACE)
    {
        const_cast<Thread*>(cur)->flags.IRQsetUserspace(true);
        ::ctxsave=cur->userCtxsave;
        //We know it's not the kernel, so the cast is safe
        static_cast<Process*>(cur->proc)->mpu.IRQenable();
    } else {
        const_cast<Thread*>(cur)->flags.IRQsetUserspace(false);
        ::ctxsave=cur->ctxsave;
        MPUConfiguration::IRQdisable();
    }
}

bool Thread::IRQreportFault(const miosix_private::FaultData& fault)
{
    if(const_cast<Thread*>(cur)->flags.isInUserspace()==false
        || cur->proc==kernel) return false;
    //We know it's not the kernel, so the cast is safe
    static_cast<Process*>(cur->proc)->fault=fault;
    const_cast<Thread*>(cur)->flags.IRQsetUserspace(false);
    ::ctxsave=cur->ctxsave;
    MPUConfiguration::IRQdisable();
    return true;
}

#endif //WITH_PROCESSES

void Thread::threadLauncher(void *(*threadfunc)(void*), void *argv)
{
    void *result=0;
    #ifdef __NO_EXCEPTIONS
    result=threadfunc(argv);
    #else //__NO_EXCEPTIONS
    try {
        result=threadfunc(argv);
    } catch(std::exception& e) {
        errorLog("***An exception propagated through a thread\n");
        errorLog("what():%s\n",e.what());
    } catch(...) {
        errorLog("***An exception propagated through a thread\n");
    }
    #endif //__NO_EXCEPTIONS
    //Thread returned from its entry point, so delete it

    //Since the thread is running, it cannot be in the sleeping_list, so no need
    //to remove it from the list
    {
        FastInterruptDisableLock lock;
        const_cast<Thread*>(cur)->flags.IRQsetDeleted();

        if(const_cast<Thread*>(cur)->flags.isDetached()==false)
        {
            //If thread is joinable, handle join
            if(cur->joinData.waitingForJoin!=NULL)
            {
                //Wake thread
                cur->joinData.waitingForJoin->flags.IRQsetJoinWait(false);
            }
            //Set result
            cur->joinData.result=result;
        } else {
            //If thread is detached, memory can be deallocated immediately
            exist_deleted=true;
        }
    }
    Thread::yield();//Since the thread is now deleted, yield immediately.
    //Will never reach here
    errorHandler(UNEXPECTED);
}

Thread *Thread::allocateIdleThread()
{
    //NOTE: this function is only called once before the kernel is started, so
    //there are no concurrency issues, not even with interrupts
    
    // Create the idle and main thread
    auto *idle=Thread::doCreate(idleThread,STACK_IDLE,NULL,Thread::DEFAULT,true);
    if(idle==nullptr) errorHandler(OUT_OF_MEMORY);
    
    // cur must point to a valid thread, so we make it point to the the idle one
    cur=idle;
    return idle;
}

struct _reent *Thread::getCReent()
{
    return getCurrentThread()->cReentrancyData;
}

#ifdef WITH_PROCESSES

miosix_private::SyscallParameters Thread::switchToUserspace()
{
    miosix_private::portableSwitchToUserspace();
    miosix_private::SyscallParameters result(cur->userCtxsave);
    return result;
}

Thread *Thread::createUserspace(void *(*startfunc)(void *), void *argv,
                    unsigned short options, Process *proc)
{
    Thread *thread=doCreate(startfunc,SYSTEM_MODE_PROCESS_STACK_SIZE,argv,
            options,false);
    if(thread==NULL) return NULL;

    unsigned int *base=thread->watermark;
    try {
        thread->userCtxsave=new unsigned int[CTXSAVE_SIZE];
    } catch(std::bad_alloc&) {
        thread->~Thread();
        free(base); //Delete ALL thread memory
        return NULL;//Error
    }
    
    thread->proc=proc;
    thread->flags.IRQsetWait(true); //Thread is not yet ready
    
    //Add thread to thread list
    {
        //Handling the list of threads, critical section is required
        PauseKernelLock lock;
        if(Scheduler::PKaddThread(thread,MAIN_PRIORITY)==false)
        {
            //Reached limit on number of threads
            base=thread->watermark;
            thread->~Thread();
            free(base); //Delete ALL thread memory
            return NULL;
        }
    }

    return thread;
}

void Thread::setupUserspaceContext(unsigned int entry, unsigned int *gotBase,
    unsigned int ramImageSize)
{
    void *(*startfunc)(void*)=reinterpret_cast<void *(*)(void*)>(entry);
    unsigned int *ep=gotBase+ramImageSize/sizeof(int);
    miosix_private::initCtxsave(cur->userCtxsave,startfunc,ep,0,gotBase);
}

#endif //WITH_PROCESSES

Thread::Thread(unsigned int *watermark, unsigned int stacksize,
               bool defaultReent) : schedData(), flags(), savedPriority(0),
               mutexLocked(0), mutexWaiting(0), watermark(watermark),
               ctxsave(), stacksize(stacksize)
{
    joinData.waitingForJoin=NULL;
    if(defaultReent) cReentrancyData=_GLOBAL_REENT;
    else {
        cReentrancyData=new _reent;
        if(cReentrancyData) _REENT_INIT_PTR(cReentrancyData);
    }
    #ifdef WITH_PROCESSES
    proc=kernel;
    userCtxsave=nullptr;
    #endif //WITH_PROCESSES
}

Thread::~Thread()
{
    if(cReentrancyData && cReentrancyData!=_GLOBAL_REENT)
    {
        _reclaim_reent(cReentrancyData);
        delete cReentrancyData;
    }
    #ifdef WITH_PROCESSES
    if(userCtxsave) delete[] userCtxsave;
    #endif //WITH_PROCESSES
}

//
// class ThreadFlags
//

void Thread::ThreadFlags::IRQsetWait(bool waiting)
{
    if(waiting) flags |= WAIT; else flags &= ~WAIT;
    Scheduler::IRQwaitStatusHook();
}

void Thread::ThreadFlags::IRQsetJoinWait(bool waiting)
{
    if(waiting) flags |= WAIT_JOIN; else flags &= ~WAIT_JOIN;
    Scheduler::IRQwaitStatusHook();
}

void Thread::ThreadFlags::IRQsetCondWait(bool waiting)
{
    if(waiting) flags |= WAIT_COND; else flags &= ~WAIT_COND;
    Scheduler::IRQwaitStatusHook();
}

void Thread::ThreadFlags::IRQsetSleep(bool sleeping)
{
    if(sleeping) flags |= SLEEP; else flags &= ~SLEEP;
    Scheduler::IRQwaitStatusHook();
}

void Thread::ThreadFlags::IRQsetDeleted()
{
    flags |= DELETED;
    Scheduler::IRQwaitStatusHook();
}

} //namespace miosix
