///
///	The scheduler function
///
UThread& UScheduler::Schedule()
{

	UThread& nextThread = DequeueNextReadyThread();

	DebugExec(GetRunningThread().SetTimestamp(-1));

	RenewThread(nextThread);

	return nextThread;

}
Beispiel #2
0
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();
}
Beispiel #3
0
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,&current,&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,&currentThread,&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(&currentThread,&nextThread);

			DebugAssertFalse(currentThread._node.IsInList());

			DebugAssertEquals(0,GetLockCount());

			DebugAssertEquals(&currentThread,(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());

}
Beispiel #8
0
void Thread::StackTrace(int, const char**)
{
	GetRunningThread()->fThreadContext.PrintStackTrace();
}
Beispiel #9
0
bool Thread::CopyUser(void *dest, const void *src, int size)
{
	ASSERT(GetRunningThread() == this);

	return CopyUserInternal(dest, src, size, &fFaultHandler);
}