📄 kern_thread.cxx
字号:
/* * Copyright (C) 1998, 1999, 2001, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * 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, * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <kerninc/kernel.hxx>#include <eros/Key.h>#include <kerninc/Thread.hxx>#include <kerninc/Check.hxx>#include <kerninc/CpuReserve.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/Machine.hxx>#include <kerninc/SysTimer.hxx>#include <arch-kerninc/KernTune.hxx>#include <kerninc/util.h>#include <kerninc/Node.hxx>#include <kerninc/Process.hxx>#include <kerninc/PhysMem.hxx>const int SCHED_QUANTA = 10;#define PREEMPTION/* #define THREADDEBUG */const char *Thread::stateNames[Thread::NUM_STATES] = { "Free", "Ready", "Running", "Stalled", "IoCompleted",};#if 0/* These definitions were moved to arch/XXX/kernel/IPC-vars.cxx for cache * line locality */Thread* Thread::curThread;bool Thread::threadShouldYield;bool Thread::cannotPreempt;jmp_buf Thread::ThreadRecoveryBlock;#endifThread *Thread::ThreadTable;Thread *Thread::NextFreeThread;#if 0static ThreadPile freeList;#endifThreadPile ProcessorQueue;ThreadPile IdlePile;uint32_t LowestRunningPriority;/* This constructor used when fabricating new threads in IT_Send. * Reserve field will get populated when thread migrates to receiving * context. */Thread::Thread(){ context = 0; errCode = 0; ioCount = 0; state = Thread::Stall; nRun = 0; cpuReserve = 0;}/* Called only for kernel threads */Thread::Thread(Process* pContext){ context = pContext; errCode = 0; ioCount = 0; state = Thread::Stall; nRun = 0; cpuReserve = 0; /* CAREFUL -- reserve cannot be defined here, because of constructor * order!!! */}/* Note -- at one point I tried to make all threads have domain keys, * and inoperative threads have domain keys with bad values. This * DOES NOT WORK, because if the root of the process is rescinded with * a prepared key in an outstanding thread that key will get zeroed. * Yuck!. */voidThread::AllocUserThreads(){ /* Must NOT use the overloaded new() operator!!!!! */ ThreadTable = new (0) Thread[KTUNE_NTHREAD]; NextFreeThread = 0; for (int i = 0; i < KTUNE_NTHREAD; i++) { Thread *t = &ThreadTable[i]; t->state = Thread::Free; t->processKey.IKS_VoidKey(); delete t; } printf("Allocated User Threads: 0x%x at 0x%08x\n", sizeof(Thread[KTUNE_NTHREAD]), ThreadTable);}void *Thread::operator new(size_t /* sz */){ if (NextFreeThread) { Thread *t = NextFreeThread; NextFreeThread = (Thread *) NextFreeThread->next; t->next = 0; return t; } fatal("Threads exhausted\n"); return 0;}voidThread::operator delete(void *v){ Thread *t = (Thread *) v; /* dprintf(true, "Deleting thread 0x%08x\n", t); */ /* not hazarded because thread key */ t->processKey.NH_VoidKey(); t->reserveLinkage.Unlink(); t->state = Thread::Free; if (curThread == t) {#if 0 printf("curthread 0x%08x is deleted\n", t);#endif curThread = 0; } /* This must be done after the unlink, since we are using the field * in a way that the Link structure doesn't know about. */ t->next = NextFreeThread; NextFreeThread = t;}#ifndef NDEBUGboolThread::ValidThreadKey(const Key* pKey){ int i; for (i = 0; i < KTUNE_NTHREAD; i++) { if ( &ThreadTable[i].processKey == pKey ) return true; } return false;}#endifvoidThread::SleepOn(ThreadPile& p){ IRQ::DISABLE();#if defined(DBG_WILD_PTR) if (dbg_wild_ptr && 0) Check::Consistency("In Thread::SleepOn()");#endif if (state != Running && prev) fatal("Thread 0x%08x (%s) REqueues q=0x%08x lastq=0x%08x state=%d\n", this, Name(), &p, lastq, state); Thread::DO_NOT_PREEMPT(); lastq = &p; assert(next == 0); assert(prev == 0); Link* ql = &p; next = ql->next; prev = ql; ql->next = this; if (next) next->prev = this; state = Thread::Stall;#ifdef OPTION_DDB { extern bool ddb_thread_uqueue_debug; if (IsUser() && ddb_thread_uqueue_debug) dprintf(true, "Thread 0x%08x sleeps on queue 0x%08x\n", this, &p); }#endif IRQ::ENABLE();}voidThread::Delay(uint64_t ms){ assert(curThread == this); IRQ::DISABLE(); WakeUpIn(ms); SleepOn(IdlePile); IRQ::ENABLE(); Yield();}/* Threads only use the timer system when they are about to yield, * sleep, so do not preempt them once they set a timer. */voidThread::WakeUpIn(uint64_t ms){ Thread::DO_NOT_PREEMPT(); assert(Thread::state == Thread::Running); wakeTime = SysTimer::Now() + Machine::MillisecondsToTicks(ms); assert(Thread::state == Thread::Running); SysTimer::AddSleeper(*this); assert(Thread::state == Thread::Running);}voidThread::WakeUpAtTick(uint64_t tick){ Thread::DO_NOT_PREEMPT(); wakeTime = tick; SysTimer::AddSleeper(*this);}voidThread::Wakeup(){ IRQ::DISABLE(); int priority = cpuReserve->curPrio; Unlink(); if (wakeTime) { assert ( state != Running ); SysTimer::CancelAlarm(*this); wakeTime = 0; } Link* ql = &ProcessorQueue; Thread *nextThread = (Thread *) ql->next; while (nextThread && nextThread->cpuReserve->curPrio >= priority) {#ifdef OPTION_DDB { extern bool ddb_thread_uqueue_debug; if (IsUser() && ddb_thread_uqueue_debug) dprintf(true, "Thread 0x%08x sleeps on queue 0x%08x (ProcessorQueue)\n", this, &ProcessorQueue); }#endif ql = (Link *) nextThread; nextThread = (Thread *) ql->next; } /* Either there are no more threads, or we should be inserted * after ql: */ next = ql->next; prev = ql; ql->next = this; if (next) next->prev = this; state = Thread::Ready; lastq = &ProcessorQueue; /* Do not set threadShouldYield until the first thread runs! * There may not be a curthread if we are being woken up because * the previous thread died. * assert(curThread); */ if (curThread && cpuReserve->curPrio > curThread->cpuReserve->curPrio) {#if 0 printf("Wake 0x%08x Cur thread should yield. canPreempt=%c\n", this, canPreempt ? 'y' : 'n');#endif curThread->Preempt(); } IRQ::ENABLE();#ifdef DBG_WILD_PTR if (dbg_wild_ptr) Check::Consistency("In Thread::Wakeup()");#endif}#if 0extern "C" { void resume_from_savearea(SaveArea*);}#endifvoidThread::Expired(Timer*){ Preempt(); #if 0 if (!CAN_PREEMPT()) { printf("0x%08x Quanta expires in NOPREEMPT, state %d.\n", curThread, curThread->state); }#endif}#ifdef PREEMPTION/* static Timer QuantaTimer; */#endifinline voidThread::ChooseNewCurrentThread(){ assert ( ProcessorQueue.IsEmpty() == false ); assert( IRQ::DISABLE_DEPTH() == 1 ); IRQ::DISABLE(); curThread = (Thread*) ProcessorQueue.next; assert(curThread); curThread->Unlink(); curThread->state = Thread::Running; IRQ::ENABLE(); assert( IRQ::DISABLE_DEPTH() == 1 );}/* DoReschedule() is called for a number of reasons: * * 1. No current thread * 2. Current thread preempted * 3. Current thread not prepared * 4. Current thread's context not prepared * 5. Current thread has fault code (keeper invocation needed) * * In the old logic, only thread prepare could Yield(). In the new * logic, the keeper invocation may also yield. For this reason, both * the prepare logic and the keeper invocation logic are in separate * functions for now. I am seriously contemplating integrating them * into the main code body and setting up a recovery block here. *//* FIX: Somewhere in here the context pins are not getting updated * correctly. It's not important until we do SMP. */voidThread::DoReschedule(){ assert( IRQ::DISABLE_DEPTH() == 1 );#ifdef DBG_WILD_PTR if (dbg_wild_ptr && 0) Check::Consistency("In DoReschedule()");#endif /* DoReschedule may be called from the timer interrupt when kernel * interrupts are enabled. If so, suppress if we are in a * no-preempt state, as in this event the thread will shortly yield * in any case: */ if (yieldState == (ys_ShouldYield | ys_NoPreempt)) { printf("0x%08x Preempt suppressed, state=%d\n", curThread, curThread->state); return; } /* On the way out of an invocation trap there may be no current * thread, in which case we may need to choose a new one: */ if (curThread == 0) { ChooseNewCurrentThread(); yieldState = 0; } if (yieldState == ys_ShouldYield) { /* Current thread may be stalled or dead; if so, don't stick it * back on the run list! */ if ( curThread->state == Thread::Running ) { curThread->Wakeup(); /* perverse, but correct. */#ifdef THREADDEBUG if ( curThread->IsUser() ) printf("Active thread goes to end of run queue\n");#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -