/// /// The scheduler function /// UThread& UScheduler::Schedule() { UThread& nextThread = DequeueNextReadyThread(); DebugExec(GetRunningThread().SetTimestamp(-1)); RenewThread(nextThread); return nextThread; }
Thread::Thread(const char name[], Team *team, thread_start_t startAddress, void *param, int priority) : Resource(OBJ_THREAD, name), fThreadContext(team->GetAddressSpace()->GetPhysicalMap()), fBasePriority(priority), fCurrentPriority(priority), fFaultHandler(0), fLastEvent(SystemTime()), fState(kThreadCreated), fTeam(team), fKernelStack(0), fUserStack(0) { char stackName[OS_NAME_LENGTH]; snprintf(stackName, OS_NAME_LENGTH, "%.12s stack", name); fKernelStack = AddressSpace::GetKernelAddressSpace()->CreateArea(stackName, kKernelStackSize, AREA_WIRED, SYSTEM_READ | SYSTEM_WRITE, new PageCache, 0, INVALID_PAGE, SEARCH_FROM_TOP); if (fKernelStack == 0) { printf("team = %p\n", fTeam); panic("Can't create kernel stack for thread: out of virtual space\n"); } unsigned int kernelStack = fKernelStack->GetBaseAddress() + kKernelStackSize - 4; unsigned int userStack = 0; if (team->GetAddressSpace() != AddressSpace::GetKernelAddressSpace()) { // Create the user stack fUserStack = fTeam->GetAddressSpace()->CreateArea(stackName, kUserStackSize, AREA_NOT_WIRED, USER_READ | USER_WRITE | SYSTEM_READ | SYSTEM_WRITE, new PageCache, 0, INVALID_PAGE, SEARCH_FROM_TOP); if (fUserStack == 0) { printf("team = %p\n", fTeam); panic("Can't create user stack for thread: out of virtual space\n"); } userStack = fUserStack->GetBaseAddress() + kUserStackSize - 4; } fThreadContext.Setup(startAddress, param, userStack, kernelStack); // Inherit the current directory from the thread that created this. fCurrentDir = GetRunningThread()->fCurrentDir; if (fCurrentDir) fCurrentDir->AcquireRef(); AcquireRef(); // This reference is effectively owned by the actual thread // of execution. It will be released by the Grim Reaper team->ThreadCreated(this); gScheduler.EnqueueReadyThread(this); gScheduler.Reschedule(); }
void Thread::Exit() { ASSERT(GetRunningThread() == this); cpu_flags fl = DisableInterrupts(); SetState(kThreadDead); fReapQueue.Enqueue(this); fThreadsToReap.Release(1, false); RestoreInterrupts(fl); gScheduler.Reschedule(); panic("terminated thread got scheduled"); }
/// /// Returns TRUE when there is a ready thread with a bigger priority than the running thread. Returns FALSE otherwise. /// bool UScheduler::HaveReadyThreads() { /// /// Get the value of the next thread /// UThread* nextThread = PeekNextReadyThread(); if(nextThread == NULL) return false; /// /// Check if the next thread have a lower priority than the current thread, if so return FALSE /// if (nextThread->_threadPriority > GetRunningThread()._threadPriority) return false; return true; }
void UScheduler::SwitchContexts(Context ** trapContext) { DebugAssertTrue(!IsLocked()); DebugAssertTrue(CanScheduleThreads()); /// /// Retrieve the current thread to do the context switch. /// UThread& current = GetRunningThread(); DebugAssertFalse(current._node.IsInList()); /// /// Save the current thread context (saved on interrupts trap) /// current._context = *trapContext; /// /// Insert the current thread on ready queue, to further scheduling. /// InsertThreadInReadyQueue(current); /// /// Get the ready thread to run. /// UThread& next = Schedule(); DebugAssertNotEqualsP(UThread,¤t,&next); /// /// Set the @next thread as the running thread. /// _Scheduler._pRunningThread = &next; /// /// Switch contexts. /// *trapContext = next._context; }
void UScheduler::Timer::Trigger() { DebugAssertTrue(IsLocked()); if(!_node.IsInList()) return; /// /// if the thread is still waiting for timer event wake it up. /// if(_thread.TryLockParker()) { DebugAssertEquals(_thread.GetThreadState(),UThread::EVENT); /// /// Its possible that the timer event was triggered before the thread could /// switch to another, so to avoid control erros, check if the waiting thread already /// switched. And if not set its state to ready. This behaviour is possible because the /// pending pisrs call be called before the thread actually switchs with anoter. /// if(&GetRunningThread() != &_thread) _thread.UnparkThread(UThread::PARK_TIMEOUT); /// /// /// else { /// /// If the thread is still on UnlockInner function, set /// the park state as timeout, and the thread state to READY. /// _thread._parkerState = UThread::PARK_TIMEOUT; _thread.SetThreadState(UThread::READY); } } }
void UScheduler::UnlockInner(U32 lockCount) { DebugAssertTrue(lockCount+1 == GetLockCount() || lockCount == GetLockCount()); do { /// /// If there are pending pisrs run them. /// if(InterruptController::ArePisrsPending()) InterruptController::RunPendingPisrs(); else { UThread& currentThread = GetRunningThread(); /// /// Check if this thread already consumed its time stamp. /// if so and there are no more threads to run, renew its timestamp and return. /// if(currentThread.GetThreadState() == UThread::READY) { if(!HasCurrentThreadTimestampPassed()) break; if(!HaveReadyThreads()) { RenewThread(currentThread); break; } if(!currentThread._node.IsInList()) { InsertThreadInReadyQueue(currentThread); } } /// /// Get the next thread from the scheduler. And a reference to the current. /// UThread& nextThread = Schedule(); DebugAssertNotEqualsP(UThread,¤tThread,&nextThread); /// /// Set the next thread as the running thread. /// _Scheduler._pRunningThread = &nextThread; DebugExec( if(!currentThread._node.IsInList()) DebugAssertTrue(currentThread.GetThreadState() != UThread::READY) ); /// /// Disable interrupts so that no interrupt occur during context switching, /// that way its impossible an interrupt service routine switchs threads /// without their contexts are properly saved. /// bool prevState = System::AcquireSystemLock(); /// /// Set scheduler lock count to 0 this thread isn't doing any critical operation on /// the scheduler instance. /// SetLockCount(0); /// /// Switch threads. /// _Scheduler.ContextSwitch(¤tThread,&nextThread); DebugAssertFalse(currentThread._node.IsInList()); DebugAssertEquals(0,GetLockCount()); DebugAssertEquals(¤tThread,(void*)_Scheduler._pRunningThread); SetLockCount(lockCount); /// /// Reenable interrupts, disabled before context switching. /// System::ReleaseSystemLock(prevState); return; } }while(true); /// /// Set the scheduler lock with the value before the switch happened. /// SetLockCount(lockCount); DebugAssertFalse(GetRunningThread()._node.IsInList()); }
void Thread::StackTrace(int, const char**) { GetRunningThread()->fThreadContext.PrintStackTrace(); }
bool Thread::CopyUser(void *dest, const void *src, int size) { ASSERT(GetRunningThread() == this); return CopyUserInternal(dest, src, size, &fFaultHandler); }