You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
11 KiB
311 lines
11 KiB
5 years ago
|
#if NET20 || NET30 || NET35 || !NET_4_6
|
||
|
|
||
|
using System.Collections.Generic;
|
||
|
using System.Diagnostics.Contracts;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.ExceptionServices;
|
||
|
using LinqInternal.Core;
|
||
|
|
||
|
namespace System.Threading.Tasks
|
||
|
{
|
||
|
public partial class Task
|
||
|
{
|
||
|
private Action _promiseCheck = ActionHelper.GetNoopAction();
|
||
|
|
||
|
internal Task(TaskStatus taskStatus, InternalTaskOptions internalTaskOptions)
|
||
|
{
|
||
|
_status = (int)taskStatus;
|
||
|
_internalOptions = internalTaskOptions;
|
||
|
ExecutingTaskScheduler = TaskScheduler.Default;
|
||
|
}
|
||
|
|
||
|
internal Task()
|
||
|
{
|
||
|
_status = (int)TaskStatus.WaitingForActivation;
|
||
|
_internalOptions = InternalTaskOptions.PromiseTask;
|
||
|
ExecutingTaskScheduler = TaskScheduler.Default;
|
||
|
}
|
||
|
|
||
|
internal Task(object state, TaskCreationOptions creationOptions)
|
||
|
{
|
||
|
if ((creationOptions & ~(TaskCreationOptions.AttachedToParent | TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
|
||
|
{
|
||
|
throw new ArgumentOutOfRangeException("creationOptions");
|
||
|
}
|
||
|
// Only set a parent if AttachedToParent is specified.
|
||
|
if ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
|
||
|
{
|
||
|
_parent = InternalCurrent;
|
||
|
}
|
||
|
State = state;
|
||
|
_creationOptions = creationOptions;
|
||
|
_status = (int)TaskStatus.WaitingForActivation;
|
||
|
_internalOptions = InternalTaskOptions.PromiseTask;
|
||
|
ExecutingTaskScheduler = TaskScheduler.Default;
|
||
|
}
|
||
|
|
||
|
public static Task FromCanceled(CancellationToken cancellationToken)
|
||
|
{
|
||
|
return FromCancellation(cancellationToken);
|
||
|
}
|
||
|
|
||
|
public static Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)
|
||
|
{
|
||
|
return FromCancellation<TResult>(cancellationToken);
|
||
|
}
|
||
|
|
||
|
public static Task<TResult> FromResult<TResult>(TResult result)
|
||
|
{
|
||
|
return new Task<TResult>(TaskStatus.RanToCompletion, InternalTaskOptions.DoNotDispose)
|
||
|
{
|
||
|
CancellationToken = default(CancellationToken),
|
||
|
InternalResult = result
|
||
|
};
|
||
|
}
|
||
|
|
||
|
internal static Task<TResult> FromCancellation<TResult>(CancellationToken token)
|
||
|
{
|
||
|
var result = new Task<TResult>(TaskStatus.WaitingForActivation, InternalTaskOptions.PromiseTask)
|
||
|
{
|
||
|
CancellationToken = token,
|
||
|
ExecutingTaskScheduler = TaskScheduler.Default
|
||
|
};
|
||
|
if (token.IsCancellationRequested)
|
||
|
{
|
||
|
result.InternalCancel(false);
|
||
|
}
|
||
|
else if (token.CanBeCanceled)
|
||
|
{
|
||
|
token.Register(() => result.InternalCancel(false));
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal static Task FromCancellation(CancellationToken token)
|
||
|
{
|
||
|
var result = new Task(TaskStatus.WaitingForActivation, InternalTaskOptions.PromiseTask)
|
||
|
{
|
||
|
CancellationToken = token,
|
||
|
ExecutingTaskScheduler = TaskScheduler.Default
|
||
|
};
|
||
|
if (token.IsCancellationRequested)
|
||
|
{
|
||
|
result.InternalCancel(false);
|
||
|
}
|
||
|
else if (token.CanBeCanceled)
|
||
|
{
|
||
|
token.Register(() => result.InternalCancel(false));
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal bool SetCompleted(bool preventDoubleExecution)
|
||
|
{
|
||
|
// For this method to be called the Task must have been scheduled,
|
||
|
// this means that _status must be at least TaskStatus.WaitingForActivation (1),
|
||
|
// if status is:
|
||
|
// TaskStatus.WaitingForActivation (1) -> ok
|
||
|
// WaitingToRun (2) -> ok
|
||
|
// TaskStatus.Running (3) -> ok if preventDoubleExecution = false
|
||
|
// TaskStatus.WaitingForChildrenToComplete (4) -> ok if preventDoubleExecution = false
|
||
|
// TaskStatus.RanToCompletion (5) -> ok if preventDoubleExecution = false
|
||
|
// TaskStatus.Canceled (6) -> not ok
|
||
|
// TaskStatus.Faulted (7) -> -> ok if preventDoubleExecution = false
|
||
|
var spinWait = new SpinWait();
|
||
|
while (true)
|
||
|
{
|
||
|
var lastValue = Thread.VolatileRead(ref _status);
|
||
|
if ((preventDoubleExecution && lastValue >= 3) || lastValue == 6)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
var tmp = Interlocked.CompareExchange(ref _status, 5, lastValue);
|
||
|
if (tmp == lastValue)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
spinWait.SpinOnce();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void SetPromiseCheck(Action value)
|
||
|
{
|
||
|
_promiseCheck = value ?? ActionHelper.GetNoopAction();
|
||
|
}
|
||
|
|
||
|
internal bool TrySetCanceled(CancellationToken cancellationToken)
|
||
|
{
|
||
|
if (IsCompleted)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
CancellationToken = cancellationToken;
|
||
|
try
|
||
|
{
|
||
|
// If an unstarted task has a valid CancellationToken that gets signalled while the task is still not queued
|
||
|
// we need to proactively cancel it, because it may never execute to transition itself.
|
||
|
// The only way to accomplish this is to register a callback on the CT.
|
||
|
|
||
|
if (cancellationToken.IsCancellationRequested)
|
||
|
{
|
||
|
// Fast path for an already-canceled cancellationToken
|
||
|
InternalCancel(false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Regular path for an uncanceled cancellationToken
|
||
|
var registration = cancellationToken.Register(_taskCancelCallback, this);
|
||
|
_cancellationRegistration = new StrongBox<CancellationTokenRegistration>(registration);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
catch (Exception)
|
||
|
{
|
||
|
// If we have an exception related to our CancellationToken, then we need to subtract ourselves
|
||
|
// from our parent before throwing it.
|
||
|
if
|
||
|
(
|
||
|
(_parent != null)
|
||
|
&& ((_creationOptions & TaskCreationOptions.AttachedToParent) != 0)
|
||
|
&& ((_parent._creationOptions & TaskCreationOptions.DenyChildAttach) == 0)
|
||
|
)
|
||
|
{
|
||
|
_parent.DisregardChild();
|
||
|
}
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal bool TrySetCanceledPromise(CancellationToken tokenToRecord)
|
||
|
{
|
||
|
Contract.Assert(Action == null);
|
||
|
var returnValue = false;
|
||
|
Contract.Assert(IsPromiseTask, "Task.RecordInternalCancellationRequest(CancellationToken) only valid for promise-style task");
|
||
|
Contract.Assert(CancellationToken == default(CancellationToken));
|
||
|
if (tokenToRecord != default(CancellationToken))
|
||
|
{
|
||
|
CancellationToken = tokenToRecord;
|
||
|
}
|
||
|
returnValue |= InternalCancel(false);
|
||
|
return returnValue;
|
||
|
}
|
||
|
|
||
|
internal bool TrySetException(Exception exception)
|
||
|
{
|
||
|
AddException(exception);
|
||
|
var status = Interlocked.CompareExchange(ref _status, (int)TaskStatus.Faulted, (int)TaskStatus.WaitingForActivation);
|
||
|
var succeeded = status == (int)TaskStatus.WaitingForActivation;
|
||
|
if (succeeded)
|
||
|
{
|
||
|
MarkCompleted();
|
||
|
FinishStageThree();
|
||
|
}
|
||
|
return succeeded;
|
||
|
}
|
||
|
|
||
|
internal bool TrySetException(IEnumerable<Exception> exceptions)
|
||
|
{
|
||
|
foreach (var exception in exceptions)
|
||
|
{
|
||
|
AddException(exception);
|
||
|
}
|
||
|
var status = Interlocked.CompareExchange(ref _status, (int)TaskStatus.Faulted, (int)TaskStatus.WaitingForActivation);
|
||
|
var succeeded = status == (int)TaskStatus.WaitingForActivation;
|
||
|
if (succeeded)
|
||
|
{
|
||
|
MarkCompleted();
|
||
|
FinishStageThree();
|
||
|
}
|
||
|
return succeeded;
|
||
|
}
|
||
|
|
||
|
internal bool TrySetException(IEnumerable<ExceptionDispatchInfo> exceptions)
|
||
|
{
|
||
|
foreach (var exception in exceptions)
|
||
|
{
|
||
|
AddException(exception);
|
||
|
}
|
||
|
var status = Interlocked.CompareExchange(ref _status, (int)TaskStatus.Faulted, (int)TaskStatus.WaitingForActivation);
|
||
|
var succeeded = status == (int)TaskStatus.WaitingForActivation;
|
||
|
if (succeeded)
|
||
|
{
|
||
|
MarkCompleted();
|
||
|
FinishStageThree();
|
||
|
}
|
||
|
return succeeded;
|
||
|
}
|
||
|
|
||
|
private static Task CreateCompletedTask()
|
||
|
{
|
||
|
return new Task(TaskStatus.RanToCompletion, InternalTaskOptions.DoNotDispose)
|
||
|
{
|
||
|
CancellationToken = default(CancellationToken)
|
||
|
};
|
||
|
}
|
||
|
|
||
|
private void PromiseCheck()
|
||
|
{
|
||
|
_promiseCheck.Invoke();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public partial class Task<TResult>
|
||
|
{
|
||
|
internal Task(TaskStatus taskStatus, InternalTaskOptions internalTaskOptions)
|
||
|
: base(taskStatus, internalTaskOptions)
|
||
|
{
|
||
|
// Empty
|
||
|
}
|
||
|
|
||
|
internal Task()
|
||
|
{
|
||
|
// Empty
|
||
|
}
|
||
|
|
||
|
internal Task(object state, TaskCreationOptions creationOptions)
|
||
|
: base(state, creationOptions)
|
||
|
{
|
||
|
// Empty
|
||
|
}
|
||
|
|
||
|
public new static Task<TResult> FromCancellation(CancellationToken token)
|
||
|
{
|
||
|
var result = new Task<TResult>(TaskStatus.WaitingForActivation, InternalTaskOptions.PromiseTask)
|
||
|
{
|
||
|
CancellationToken = token,
|
||
|
ExecutingTaskScheduler = TaskScheduler.Default
|
||
|
};
|
||
|
if (token.IsCancellationRequested)
|
||
|
{
|
||
|
result.InternalCancel(false);
|
||
|
}
|
||
|
else if (token.CanBeCanceled)
|
||
|
{
|
||
|
token.Register(() => result.InternalCancel(false));
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
internal bool TrySetResult(TResult result)
|
||
|
{
|
||
|
if (IsFaulted)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if (IsCanceled)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if (SetCompleted(true))
|
||
|
{
|
||
|
InternalResult = result;
|
||
|
MarkCompleted();
|
||
|
FinishStageThree();
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|