////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: LaxityThread.cs
//
// Note:
//
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Singularity;
using Microsoft.Singularity.Scheduling;
namespace Microsoft.Singularity.Scheduling.Laxity
{
///
/// 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 it’s waiting on (for use in scheduling inheritance), the basic thread
/// queue and sleep queue pointers, lists of people waiting on mutexes and cv’s
/// owned by this thread, bookkeeping for the type of waiting that I’m doing, and
/// general bookkeeping.
///
public class LaxityThread : ISchedulerThread
{
private static ListNode SleepingThreads = null; // list of sleeping threads
private static DateTime SleepTimeout = DateTime.MaxValue; // Next thread wakeup time
private TimeSpan executionTime;
public override TimeSpan ExecutionTime
{
get { return executionTime; }
}
public LaxityActivity AssociatedActivity; // Thread Activity
//public LaxityThread.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 -- corresponds to MMID Tid
//TODO: Replace with TASK public int Tid; // Id to mark node reservations made for this Constraint Stack
// 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 LaxityThread Next; // Next and previous threads
public LaxityThread Previous;
// DI -- maybe don't use it, see vtlb.c
// UINT QueueType; // Type of queue thread is on, if any.
// 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 LaxityProcessor ActiveProcessor; //Set only when running on processor -- no other processor may take the thread to run.
//public interface
public LaxityThread(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 = LaxityThread.ThreadState.ThreadWaiting;
}
#region ISchedulerThread Members
public override Thread EnclosingThread
{
get { return enclosingThread; }
}
//public interface
public override void SetActivity(ISchedulerActivity iActivityNew)
{
DebugStub.Print("LaxityThread.SetActivity()\n");
Debug.Assert(iActivityNew != null);
Debug.Assert(iActivityNew is LaxityActivity);
Debug.Assert(((LaxityActivity)iActivityNew).ReferenceCount > 0);
LaxityActivity activityNew = (LaxityActivity)iActivityNew;
LaxityActivity activityOld = AssociatedActivity;
if (activityOld == activityNew)
return;
bool iflag = Processor.DisableInterrupts();
if (activityOld != null) {
//if (State != LaxityThread.ThreadState.ThreadWaiting)
if (enclosingThread.ThreadState == System.Threading.ThreadState.Running)
LaxityScheduler.DequeueRunThread(this);
LaxityScheduler.ActivityReleaseProtected(activityOld);
}
if (activityNew != null) {
//if (State != LaxityThread.ThreadState.ThreadWaiting)
if (enclosingThread.ThreadState == System.Threading.ThreadState.Running) {
AssociatedActivity = activityNew;
LaxityScheduler.EnqueueRunThread(this, false);
}
LaxityScheduler.ActivityObjAddRef(activityNew);
}
else if (this == LaxityScheduler.GetCurrentThread()) {
//TODO: Double Check! //State == LaxityThread.ThreadState.ThreadRunning)
if (this == LaxityScheduler.GetCurrentThread() && activityOld != null) {
LaxityScheduler.UpdateSchedulingStatus();
}
}
AssociatedActivity = activityNew;
DebugStub.Print("Exiting LaxityThread.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 LaxityThread\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();
}
LaxityScheduler.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);
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() > LaxityScheduler.MinSlice) {
Scheduler.LogSleepAdd();
StartTime = timeOut;
PutOnSleepQueue();
}
else {
//Sleeping for less than MinSlice is treated as not sleeping/yield.
if (PendingReservation != null) {
ResolvePendingReservation();
}
return;
}
}
LaxityScheduler.DequeueRunThread(this); // from RunnableThreads
}
// 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);
LaxityScheduler.EnqueueRunThread(this, true);
}
// DI -- this is called only from PutThreadOnSleepQueue
// DI -- merge them ??
static ListNode InsertThreadOnSleepQueue(LaxityThread thread, ListNode listThreadHead)
{
LaxityThread 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() > Node.MinSlice)
// {
//
// Scheduler.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);
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);
}
}
// DI -- safe
public override void Wake()
{
bool iflag = Processor.DisableInterrupts();
PullThreadOffSleepQueue();
IReady();
if (PendingReservation != null) {
ResolvePendingReservation();
}
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 = LaxityScheduler.IpcAllocateReservation();
if (reservation == null) {
reservation = LaxityScheduler.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) {
FreeReservation = LaxityScheduler.AllocateReservation();
}
}
// Free the Reservations associated with a thread.
// Used by thread cleanup.
public void IFreeConstraints()
{
OneShotReservation reservation;
// Debug.Assert(!Processor.InterruptsDisabled());
if (FreeReservation != null) {
LaxityScheduler.IpcFreeReservation(FreeReservation);
}
while ((reservation = ReservationStack) != null) {
ReservationStack = reservation.SurroundingReservation;
LaxityScheduler.ReleaseReservation(reservation);
}
}
// DI -- safe but optimize to because don't need QueueType
public ListNode Dequeue()
{
return Queue.ListRemove();
}
public static ListNode QueueFifo(LaxityThread thread, ListNode threadHead)
{
if (threadHead != null) {
thread.Queue.InsertIntoList(threadHead.Previous);
}
else {
threadHead = thread.Queue;
}
return threadHead;
}
// DI -- safe
public static LaxityThread GetThreadFromListNode(ListNode listNode)
{
Debug.Assert(listNode == null || listNode.Data is LaxityThread);
if (listNode == null) {
return null;
}
if (listNode.Data is LaxityThread) {
return (LaxityThread)listNode.Data;
}
else {
return null;
}
}
public static void WakeThreads()
{
LaxityThread ptemp;
Debug.Assert(Processor.InterruptsDisabled());
while ((ptemp = LaxityThread.GetThreadFromListNode(SleepingThreads)) != null &&
ptemp.StartTime < LaxityScheduler.SchedulingTime + LaxityScheduler.LA_Time) {
//TODO: LA_Time here?
SleepingThreads = SleepingThreads.ListRemove();
ptemp.IReady();
if (ptemp.PendingReservation != null) {
ptemp.ResolvePendingReservation();
}
Scheduler.LogWakeThread(ptemp.EnclosingThread);
}
if (ptemp != null) {
SleepTimeout = ptemp.StartTime;
}
else {
SleepTimeout = DateTime.MaxValue;
}
}
///
///
///
///
///
///
/// TODO: UNUSED
///
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;
}
}
}