//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Task.cs // // Note: // using System; using System.Diagnostics; using System.Collections; using System.Threading; #if SIMULATOR using Thread = Microsoft.Singularity.Scheduling.Thread; #endif namespace Microsoft.Singularity.Scheduling { /// /// A Singularity Task object represents work requiring a set of resources to be performed by a deadline. /// public class Task { /// /// True if task was successfully admitted /// public bool Admitted { get { return admitted; } } internal bool admitted; internal Task parentTask; private DateTime deadline; internal Activity activity; private Hashtable resourceEstimates; internal Hashtable resourcesGranted; internal Hashtable resourcesUsed; internal ISchedulerTask schedulerTask; /// /// To begin a constraint, use Task.BeginConstraint. The Task constructors /// are to be used only within the system, and perform no reservation /// activities. /// /// This constructor makes a task as a subtask of CurrentTask() and /// CurrentActivity() /// internal Task(Hashtable resourceEstimates, Hashtable resourcesGranted, DateTime deadline, bool admitted, ISchedulerTask schedulerTask) : this(resourceEstimates, resourcesGranted, deadline, admitted, schedulerTask, Scheduler.CurrentTask(), Scheduler.CurrentActivity()) { } /// /// To begin a constraint, use Task.BeginConstraint. The Task constructors /// are to be used only within the system, and perform no reservation /// activities. /// /// This constructor makes a task as a subtask of parentTask (possibly null) /// and activity (likely a new resource container). /// internal Task(Hashtable resourceEstimates, Hashtable resourcesGranted, DateTime deadline, bool admitted, ISchedulerTask schedulerTask, Task parentTask, Activity activity) { this.activity = activity; this.parentTask = parentTask; this.resourceEstimates = resourceEstimates; this.resourcesGranted = (resourcesGranted == null) ? new Hashtable() : resourcesGranted; this.deadline = deadline; this.resourcesUsed = new Hashtable(); this.admitted = admitted; this.schedulerTask = schedulerTask; } public void ClearSchedulerTask() { schedulerTask = null; } public void UpdateSchedulingState(bool admitted, DateTime deadline, Hashtable resourcesGranted) { this.admitted = admitted; this.deadline = deadline; this.resourcesGranted = resourcesGranted; } //Because there are no timed-wait-and-begin-constraint (i.e. future constraints), // I had to modify some of the simfiles to keep threads from exiting early. // Currently start-times later than now are treated as starting now. public static Task BeginTask() { Hashtable foo; return BeginTask(null, DateTime.MaxValue, null, out foo); } public static Task BeginTask(Task taskToEnd, out Hashtable used) { return BeginTask(null, DateTime.MaxValue, taskToEnd, out used); } public static Task BeginTask(Hashtable resourceEstimates, DateTime deadline) { Hashtable foo; return BeginTask(resourceEstimates, deadline, null, out foo); } public static Task BeginTask(Hashtable resourceEstimates, DateTime deadline, Task taskToEnd, out Hashtable actualUsed) { //In a single resource world, it is sufficient here to // ask the CpuProvider to begin the constraint. However, // this code will be the beginning of the starting point // for multi-resource scheduling in this capacity. ISchedulerTask schedulerTask; Task task; if ((resourceEstimates != null && resourceEstimates.Count > 0) || (Scheduler.CurrentTask().resourceEstimates != null && Scheduler.CurrentTask().resourceEstimates.Count > 0)) { bool admitted = CpuResource.Provider().BeginConstraint(resourceEstimates, deadline, taskToEnd, out schedulerTask); if (taskToEnd == null) { task = new Task(resourceEstimates, schedulerTask.ResourcesGranted, deadline, admitted, schedulerTask); } else { task = new Task(resourceEstimates, schedulerTask.ResourcesGranted, deadline, admitted, schedulerTask, taskToEnd.parentTask, taskToEnd.activity); } schedulerTask.EnclosingTask = task; } else { if (taskToEnd != null) { taskToEnd.End(); } task = new Task(null, null, DateTime.MaxValue, true, null); } if (taskToEnd != null) { actualUsed = taskToEnd.resourcesUsed; } else { actualUsed = null; } Thread.CurrentThread.currentTask = task; Debug.Assert(task.resourcesUsed.Count == 0); return task; } public static Task BeginDelayedTask(Hashtable resourceEstimates, TimeSpan relativeDeadline) { Hashtable foo; return BeginDelayedTask(resourceEstimates, relativeDeadline, null, out foo); } /// /// Delayed tasks are used for such things as atomic wait and begin constraints. /// The interpretation is that when a thread is woken up, the scheduler promises /// to resolve any pending constraints atomically with the wakeup. After calling /// BeginDelayedTask, the task won't actually begin until the thread performs /// some action that causes a sleep/wait and is being woken up from it. /// /// Example uses: /// /// while (doingSomeStuff) { /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); /// autoResetEvent.Wait(); /// if (t.Admitted) { /// //do full work /// } else { /// //do less work is possible /// } /// t.End(); /// } /// /// while (doingSomeStuff) { /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); /// Thread.Sleep(500); /// if (t.Admitted) { /// //do full work /// } else { /// //do less work is possible /// } /// t.End(); /// } /// /// while (doingSomeStuff) { /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); /// Thread.Sleep(500); /// if (t.Admitted) { /// //do full work /// } else { /// //do less work is possible /// } /// t.End(); /// } /// /// Mutex m; /// while (doingSomeStuff) { /// Task t = BeginDelayedTask(resourceEstimates, relativeDeadline); /// m.Acquire(); /// if (t.Admitted) { /// //do full work /// } else { /// //do less work is possible /// } /// t.End(); /// } /// /// /// /// /// /// /// public static Task BeginDelayedTask(Hashtable resourceEstimates, TimeSpan relativeDeadline, Task taskToEnd, out Hashtable actualUsed) { //In a single resource world, it is sufficient here to // ask the CpuProvider to begin the constraint. However, // this code will be the beginning of the starting point // for multi-resource scheduling in this capacity. ISchedulerTask schedulerTask; Task task; Debug.Assert((resourceEstimates != null && resourceEstimates.Count > 0) || (Scheduler.CurrentTask().resourceEstimates != null && Scheduler.CurrentTask().resourceEstimates.Count > 0)); CpuResource.Provider().BeginDelayedConstraint(resourceEstimates, relativeDeadline, taskToEnd, out schedulerTask); Debug.Assert(schedulerTask != null); if (taskToEnd == null) { task = new Task(resourceEstimates, schedulerTask.ResourcesGranted, DateTime.MaxValue, false, schedulerTask); } else { task = new Task(resourceEstimates, schedulerTask.ResourcesGranted, DateTime.MaxValue, false, schedulerTask, taskToEnd.parentTask, taskToEnd.activity); } schedulerTask.EnclosingTask = task; if (taskToEnd != null) { actualUsed = taskToEnd.resourcesUsed; } else { actualUsed = null; } Thread.CurrentThread.currentTask = task; Debug.Assert(task.resourcesUsed.Count == 0); return task; } /// /// The common case method for indicating task completion /// /// Returns the actual amounts of resources consumed public Hashtable End() { //Do we want this static -- i.e. to prevent the thread from trying // to end a task it's not presently working on? Debug.Assert(this == Scheduler.CurrentTask(), "Cannot end a task other than the current task!"); Debug.Assert(this != Activity().DefaultTask(), "Cannot end the default task for the resource container!"); CpuResource.Provider().EndConstraint(this); Thread.CurrentThread.currentTask = parentTask; return resourcesUsed; // XXX TBD } /// /// Return the Activity object that this task is part of /// public Activity Activity() { return activity; } public void AddResourceAmountUsed(string resourceString, IResourceAmount amount) { IResourceAmount current = (IResourceAmount)resourcesUsed[resourceString]; if (current == null) { resourcesUsed[resourceString] = amount; } else { ((IResourceAmount)resourcesUsed[resourceString]).AddTo(amount); } if (parentTask != null) { parentTask.AddResourceAmountUsed(resourceString, amount); } } } }