singrdk/base/Kernel/Singularity/Scheduling/Rialto/RialtoThread.cs

555 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: RialtoThread.cs
//
// Note:
//
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Scheduling;
namespace Microsoft.Singularity.Scheduling.Rialto
{
/// <summary>
/// The kernel structure for the thread. Contains an associated activity, a
/// stack of reservations executing the activity, a reference for a free
/// reservation (rather than allocating a new one all the time), a pending
/// reservation (used when new constraints are being created), a reference to the
/// thread its waiting on (for use in scheduling inheritance), the basic thread
/// queue and sleep queue pointers, lists of people waiting on mutexes and cvs
/// owned by this thread, bookkeeping for the type of waiting that Im doing, and
/// general bookkeeping.
/// </summary>
public class RialtoThread : ISchedulerThread
{
// Thread State values passed to SetState
// ThreadReady 0 Able to be run but not yet running
// ThreadWaiting 1 Waiting for object and/or timeout
// ThreadRunning 2 Current thread (for a given CPU)
//public enum ThreadState { ThreadReady = 0, ThreadWaiting = 1, ThreadRunning = 2};
static private ListNode sleepingThreads = null; // list of sleeping threads
static private DateTime sleepTimeout = DateTime.MaxValue; // Next thread wakeup time
private TimeSpan executionTime;
public override TimeSpan ExecutionTime
{
get { return executionTime; }
}
public RialtoActivity AssociatedActivity; // Thread Activity
//public RialtoThread.ThreadState State; // RUNNING, WAITING, SLEEPING
public OneShotReservation ReservationStack;
// Stack of Reservations, one per active Constraint
public OneShotReservation FreeReservation;
public OneShotReservation PendingReservation;
// PendingConstraint if any
// DI :: NOTE the Owner field in Kernel is used only for Inheritance control
// need to REMOVE it -- this will help to locate easy all these functions and
// comment them out
//public int Epoch; // inc-ed at every kernel unlock
public DateTime StartTime; // only while on the sleep Q
public RialtoThread Next; // Next and previous threads
public RialtoThread Previous;
// DI -- ??!! this might replace Next and Previous from above
public ListNode Queue; // List of threads in a queue
// DI -- need it as a thread may wait on a condition (i.e. use Queue)
// and wait for the timeout (Condition_TimedWait)
public ListNode SleepQueue; // Another queue link, for sleeping only
public int ReferenceCount;
public readonly Thread enclosingThread;
//public interface
public RialtoThread(Thread thread)
{
enclosingThread = thread;
Queue = new ListNode(this);
SleepQueue = new ListNode(this);
AssociatedActivity = null;
StartTime = new DateTime(0);
//TODO: BUGBUGBUG
//Tid = Id; REPLACE WITH DEFAULT TASK?
Queue.Next = Queue.Previous = Queue;
SleepQueue.Next = SleepQueue.Previous = SleepQueue;
//State = RialtoThread.ThreadState.ThreadWaiting;
}
#region ISchedulerThread Members
public override Thread EnclosingThread
{
get { return enclosingThread; }
}
//public interface
public override void SetActivity(ISchedulerActivity iActivityNew)
{
// DebugStub.Print("RialtoThread.SetActivity()\n");
Debug.Assert(iActivityNew != null);
Debug.Assert(iActivityNew is RialtoActivity);
RialtoActivity activityNew = (RialtoActivity)iActivityNew;
RialtoActivity activityOld = AssociatedActivity;
if (activityOld == activityNew) {
return;
}
bool iflag = Processor.DisableInterrupts();
if (activityOld != null) {
//if (State != RialtoThread.ThreadState.ThreadWaiting)
if (enclosingThread.ThreadState == System.Threading.ThreadState.Running)
RialtoScheduler.DequeueRunThread(this);
RialtoScheduler.ActivityReleaseProtected(activityOld);
}
if (activityNew != null) {
//if (State != RialtoThread.ThreadState.ThreadWaiting)
if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) {
AssociatedActivity = activityNew;
RialtoScheduler.EnqueueRunThread(this);
}
RialtoScheduler.ActivityObjAddRef(activityNew);
}
else if (this == RialtoScheduler.GetCurrentThread()) {
//TODO: Double Check! //State == RialtoThread.ThreadState.ThreadRunning)
if (this == RialtoScheduler.GetCurrentThread() && activityOld != null) {
RialtoScheduler.UpdateSchedulingStatus();
}
}
AssociatedActivity = activityNew;
// DebugStub.Print("Exiting RialtoThread.SetActivity()\n");
Processor.RestoreInterrupts(iflag);
}
//public interface
public override void Start()
{
//TODO: No need for assertion perhaps -- default somehow?
Debug.Assert(AssociatedActivity != null);
bool iflag = Processor.DisableInterrupts();
IReady();
Processor.RestoreInterrupts(iflag);
}
//public interface
public override void Cleanup()
{
OneShotReservation reservation;
// DebugStub.Print("Cleaning RialtoThread\n");
SetStateWaiting(DateTime.MaxValue);
// DebugStub.Print("At this point thread is dequeued.\n");
while ((reservation = ReservationStack) != null) {
ReservationStack = reservation.Next; // !!!! don't remove Caller's constraints !!!
reservation.DequeueReservation();
}
RialtoScheduler.ActivityObjRelease(AssociatedActivity);
//TODO: In the "real" system, cleanup is called by someone who will pick next thread, right?
}
#endregion
public static DateTime GetSleepTimeout()
{
if (sleepingThreads != null) {
return sleepTimeout;
}
else {
return DateTime.MaxValue;
}
}
public void AddRef()
{
ReferenceCount++;
}
// DI -- instead of calling this, one should call ::ReleaseActivity
public void Release()
{
ReferenceCount--;
Debug.Assert(ReferenceCount >= 0);
}
public void ReleaseProtected()
{
ReferenceCount--;
Debug.Assert(ReferenceCount >= 0);
}
public override void SetStateWaiting(DateTime timeOut)
{
bool iflag = Processor.DisableInterrupts();
InternalSetStateWaiting(timeOut);
RialtoScheduler.DirectSwitchOnWait();
Processor.RestoreInterrupts(iflag);
}
public void InternalSetStateWaiting(DateTime timeOut)
{
Debug.Assert(Processor.InterruptsDisabled());
// DI -- don't need activity state update
//This to handle sleeps and timed waits.
if (timeOut != DateTime.MaxValue) {
if (timeOut - SystemClock.GetKernelTime() > RialtoScheduler.MinSlice) {
Scheduler.LogSleepAdd();
StartTime = timeOut;
PutOnSleepQueue();
}
else {
//Sleeping for less than MinSlice is treated as not sleeping/yield.
if (PendingReservation != null) {
ResolvePendingReservation();
}
return;
}
}
RialtoScheduler.DequeueRunThread(this); // from RunnableThreads
if (this == RialtoScheduler.GetCurrentThread()) {
// if activity blocks during its own regular CPU slice; same code as in Sleep!
if ((AssociatedActivity.RunnableThreads == null) &&
(RialtoScheduler.CurrentPlanNode.Type == GraphNode.NodeType.Used) &&
(RialtoScheduler.CurrentPlanNode.DefaultActivity == AssociatedActivity) &&
((AssociatedActivity.MyRecurringCpuReservation.SliceLeft =
RialtoScheduler.CurrentPlanNode.NextExec + RialtoScheduler.CurrentPlanNode.Slice
- SystemClock.GetKernelTime()) >= RialtoScheduler.MinSlice)) {
AssociatedActivity.LastNode = RialtoScheduler.CurrentPlanNode; // record it
}
}
}
// DI -- this is the function in !LAXITY version
//TODO: REPLACE WITH ENCLOSING LOGIC!
//public void SetState(ThreadState newState)
//{
// State = newState;
// Debug.Assert(newState != ThreadState.ThreadWaiting);
//}
// DI -- safe
//TODO: REPLACE WITH ENCLOSING LOGIC!
void IReady()
{
Debug.Assert(Processor.InterruptsDisabled());
// TODO -- maybe something for directed context switch
// DI -- don't need PutThreadOnReadyQueue outside schedule.c
// PutThreadOnReadyQueue(thread);
RialtoScheduler.EnqueueRunThread(this);
// When an activity becomes runnable during
// its own regular CPU slice, a reschedule occurs.
if (AssociatedActivity.LastNode != null)
{
RialtoScheduler.EnqueueStandbyActivity(AssociatedActivity); // sets 'thread.AssociatedActivity.LastNode' to null
Debug.Assert(AssociatedActivity.LastNode == null && RialtoScheduler.CurrentPlanNode != null, "Removing unused code! IF EXCEPTION HAPPENS, CODE SHOULD BE REPLACED");
// if (RialtoScheduler.CurrentPlanNode == thread.AssociatedActivity.LastNode) { //TODO: This is really an equivalence with null?
// RialtoScheduler.Reschedule();
// }
}
}
// DI -- this is called only from PutThreadOnSleepQueue
// DI -- merge them ??
static ListNode InsertThreadOnSleepQueue(RialtoThread thread, ListNode listThreadHead)
{
RialtoThread threadHead, threadTail, tempThread;
if (listThreadHead == null) {
// Queue empty
return thread.SleepQueue;
}
threadHead = GetThreadFromListNode(listThreadHead);
threadTail = GetThreadFromListNode(threadHead.SleepQueue.Previous);
if (thread.StartTime >= threadTail.StartTime) {
thread.SleepQueue.InsertIntoList(threadTail.SleepQueue);
// DI -- if merge: update sleepTimeout here
return threadHead.SleepQueue;
}
if (thread.StartTime < threadHead.StartTime) {
thread.SleepQueue.InsertIntoList(threadTail.SleepQueue);
return thread.SleepQueue;
}
tempThread = GetThreadFromListNode(threadHead.SleepQueue.Next);
for (;;) {
if (tempThread.StartTime > thread.StartTime) {
thread.SleepQueue.InsertIntoList(tempThread.SleepQueue.Previous);
break;
}
tempThread = GetThreadFromListNode(tempThread.SleepQueue.Next);
}
return threadHead.SleepQueue;
}
public void PutOnSleepQueue()
{
sleepingThreads = InsertThreadOnSleepQueue(this,sleepingThreads);
sleepTimeout = (GetThreadFromListNode(sleepingThreads)).StartTime;
}
// public void Sleep(DateTime time)
// {
// if (time - SystemClock.GetKernelTime() > GraphNode.MinSlice) {
// RialtoScheduler.LogSleepAdd();
// StartTime = time;
//
// SetStateWaiting();
//
// // DI -- need it, called from WaitCond also
// PutOnSleepQueue();
// }
// }
// Di -- safe -- optimized
void PullThreadOffSleepQueue()
{
ListNode listThread, listNewHead;
listThread = SleepQueue;
listNewHead = listThread.ListRemove();
if (sleepingThreads == listThread) {
if ((sleepingThreads = listNewHead) != null) {
sleepTimeout = (GetThreadFromListNode(sleepingThreads)).StartTime;
}
else {
sleepTimeout = DateTime.MaxValue;
}
}
}
private void ResolvePendingReservation()
{
Debug.Assert(Processor.InterruptsDisabled());
Debug.Assert(PendingReservation != null);
bool admitted = OneShotReservation.ResolveConstraint(this);
//else { DebugStub.WriteLine("Wake()-Resolve returned non S_OK"); }
if (ReservationStack.EnclosingTask != null) {
// Not clear if this will work in real system or not. Here for simulator mainly.
ReservationStack.EnclosingTask.UpdateSchedulingState(admitted, ReservationStack.Deadline, ReservationStack.ResourcesGranted);
}
else {
Debug.Assert(false);
}
}
// DI -- safe
public override void Wake()
{
bool iflag = Processor.DisableInterrupts();
PullThreadOffSleepQueue();
IReady();
if (PendingReservation != null) {
ResolvePendingReservation();
}
RialtoScheduler.DirectSwitchOnWakeup();
Processor.RestoreInterrupts(iflag);
}
OneShotReservation AllocationReservation()
{
OneShotReservation reservation = FreeReservation;
Debug.Assert(reservation != null);
FreeReservation = null;
reservation.Clear();
return reservation;
}
public void IpcCheckFreeConstraint()
{
if (FreeReservation == null) {
OneShotReservation reservation;
reservation = RialtoScheduler.IpcAllocateReservation();
if (reservation == null) {
reservation = RialtoScheduler.AllocateReservation();
}
FreeReservation = reservation;
}
}
// If this thread doesn't have a free OneShotReservation reserved
// for its use, then try to grab one from the global free list.
// If there aren't any there, then allocate a new one.
void ICheckFreeConstraint()
{
if (FreeReservation == null) {
OneShotReservation reservation;
reservation = RialtoScheduler.AllocateReservation();
FreeReservation = reservation;
}
}
// Free the Reservations associated with a thread.
// Used by thread cleanup.
public void IFreeConstraints()
{
OneShotReservation reservation;
// Debug.Assert(!Processor.InterruptsDisabled());
if (FreeReservation != null) {
RialtoScheduler.IpcFreeReservation(FreeReservation);
}
while ((reservation = ReservationStack) != null) {
ReservationStack = reservation.SurroundingReservation;
RialtoScheduler.ReleaseReservation(reservation);
}
}
// DI -- safe but optimize to because don't need QueueType
public ListNode Dequeue()
{
return Queue.ListRemove();
}
public static ListNode QueueFifo(RialtoThread thread, ListNode threadHead)
{
if (threadHead != null) {
thread.Queue.InsertIntoList(threadHead.Previous);
}
else {
threadHead = thread.Queue;
}
return threadHead;
}
// DI -- safe
public static RialtoThread GetThreadFromListNode(ListNode listNode)
{
Debug.Assert(listNode == null || listNode.Data is RialtoThread);
if (listNode == null) {
return null;
}
if (listNode.Data is RialtoThread) {
return (RialtoThread)listNode.Data;
}
else {
return null;
}
}
public static void WakeThreads()
{
RialtoThread ptemp;
Debug.Assert(Processor.InterruptsDisabled());
while ((ptemp = RialtoThread.GetThreadFromListNode(sleepingThreads)) != null &&
ptemp.StartTime < RialtoScheduler.SchedulingTime + RialtoScheduler.LA_Time) {
//TODO: LA_Time here?
sleepingThreads = sleepingThreads.ListRemove();
RialtoScheduler.MoveToStandby(ptemp, true); //NOTE: Sets LastNode to null, prevents IReadythread from also EnqueueingStandbyActivity
ptemp.IReady();
if (ptemp.PendingReservation != null) {
ptemp.ResolvePendingReservation();
}
Scheduler.LogWakeThread(ptemp.EnclosingThread);
}
if (ptemp != null)
sleepTimeout = ptemp.StartTime;
else
sleepTimeout = DateTime.MaxValue;
}
/// <summary>
///
/// </summary>
/// <param name="thread"></param>
/// <param name="EndPrevious"></param>
/// <param name="timeConstraint"></param>
/// <param name="timeNow">TODO: UNUSED</param>
/// <returns></returns>
public bool BeginConstraintBeforeWaitValidate(bool endPrevious,
ref TimeConstraint timeConstraint,
DateTime timeNow)
{
if (endPrevious && // check to have a valid constraint in the stack.
(ReservationStack == null || ReservationStack.OriginalThread == null)) {
return false;
}
if (timeConstraint.Estimate.Ticks < 0) {
return false;
}
// By this time, something should be in FreeReservation
// grab it now such to enable the EndPreviousConstraint to
// save its reservation in the cache
Debug.Assert(FreeReservation != null);
Debug.Assert(FreeReservation != OneShotReservation.CurrentReservation);
PendingReservation = AllocationReservation();
return true;
}
public void AddExecutionTime(TimeSpan delta)
{
executionTime += delta;
}
public override ISchedulerTask PrepareDelayedTask(ISchedulerTask taskToEnd,
ref TimeConstraint timeConstraint,
DateTime timeNow)
{
// DI -- the Next calls are from TimedWaitAndBeginConstraint
IpcCheckFreeConstraint();
Debug.Assert(taskToEnd == null || taskToEnd == ReservationStack);
bool endPrevious = (taskToEnd != null);
if (!BeginConstraintBeforeWaitValidate(endPrevious, ref timeConstraint, timeNow)) {
goto Exit;
}
OneShotReservation.BeginConstraintBeforeWait(this, endPrevious, timeConstraint, timeNow);
Exit:
return PendingReservation;
}
}
}