////////////////////////////////////////////////////////////////////////////////
//
// 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);
}
}
}
}