Jo Shields 181b81b4a4 Imported Upstream version 3.12.0
Former-commit-id: cf92446697332992ec36726e78eb8703e1f259d7
2015-01-13 10:44:36 +00:00

393 lines
8.9 KiB
C#

//
// TaskContinuation.cs
//
// Authors:
// Jérémie Laval <jeremie dot laval at xamarin dot com>
// Marek Safar <marek.safar@gmail.com>
//
// Copyright 2011 Xamarin Inc (http://www.xamarin.com).
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
#if NET_4_0
using System.Collections.Generic;
namespace System.Threading.Tasks
{
interface IContinuation
{
void Execute ();
}
class TaskContinuation : IContinuation
{
readonly Task task;
readonly TaskContinuationOptions continuationOptions;
public TaskContinuation (Task task, TaskContinuationOptions continuationOptions)
{
this.task = task;
this.continuationOptions = continuationOptions;
}
bool ContinuationStatusCheck (TaskContinuationOptions kind)
{
if (kind == TaskContinuationOptions.None)
return true;
int kindCode = (int) kind;
var status = task.ContinuationAncestor.Status;
if (kindCode >= ((int) TaskContinuationOptions.NotOnRanToCompletion)) {
// Remove other options
kind &= ~(TaskContinuationOptions.PreferFairness
| TaskContinuationOptions.LongRunning
| TaskContinuationOptions.AttachedToParent
| TaskContinuationOptions.ExecuteSynchronously);
if (status == TaskStatus.Canceled) {
if (kind == TaskContinuationOptions.NotOnCanceled)
return false;
if (kind == TaskContinuationOptions.OnlyOnFaulted)
return false;
if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
return false;
} else if (status == TaskStatus.Faulted) {
if (kind == TaskContinuationOptions.NotOnFaulted)
return false;
if (kind == TaskContinuationOptions.OnlyOnCanceled)
return false;
if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
return false;
} else if (status == TaskStatus.RanToCompletion) {
if (kind == TaskContinuationOptions.NotOnRanToCompletion)
return false;
if (kind == TaskContinuationOptions.OnlyOnFaulted)
return false;
if (kind == TaskContinuationOptions.OnlyOnCanceled)
return false;
}
}
return true;
}
public void Execute ()
{
if (!ContinuationStatusCheck (continuationOptions)) {
task.CancelReal (notifyParent : true);
task.Dispose ();
return;
}
// The task may have been canceled externally
if (task.IsCompleted)
return;
if ((continuationOptions & TaskContinuationOptions.ExecuteSynchronously) != 0)
task.RunSynchronouslyCore (task.scheduler, false);
else
task.Schedule (false);
}
}
class AwaiterActionContinuation : IContinuation
{
readonly Action action;
public AwaiterActionContinuation (Action action)
{
this.action = action;
}
public void Execute ()
{
//
// Continuation can be inlined only when the current context allows it. This is different to awaiter setup
// because the context where the awaiter task is set to completed can be anywhere (due to TaskCompletionSource)
//
if ((SynchronizationContext.Current == null || SynchronizationContext.Current.GetType () == typeof (SynchronizationContext)) && TaskScheduler.IsDefault) {
action ();
} else {
ThreadPool.UnsafeQueueUserWorkItem (l => ((Action) l) (), action);
}
}
}
class SchedulerAwaitContinuation : IContinuation
{
readonly Task task;
public SchedulerAwaitContinuation (Task task)
{
this.task = task;
}
public void Execute ()
{
task.RunSynchronouslyCore (task.scheduler, true);
}
}
class SynchronizationContextContinuation : IContinuation
{
readonly Action action;
readonly SynchronizationContext ctx;
public SynchronizationContextContinuation (Action action, SynchronizationContext ctx)
{
this.action = action;
this.ctx = ctx;
}
public void Execute ()
{
// No context switch when we are on correct context
if (ctx == SynchronizationContext.Current)
action ();
else
ctx.Post (l => ((Action) l) (), action);
}
}
sealed class WhenAllContinuation : IContinuation
{
readonly Task owner;
readonly IList<Task> tasks;
int counter;
public WhenAllContinuation (Task owner, IList<Task> tasks)
{
this.owner = owner;
this.counter = tasks.Count;
this.tasks = tasks;
}
public void Execute ()
{
if (Interlocked.Decrement (ref counter) != 0)
return;
owner.Status = TaskStatus.Running;
bool canceled = false;
List<Exception> exceptions = null;
foreach (var task in tasks) {
if (task.IsFaulted) {
if (exceptions == null)
exceptions = new List<Exception> ();
exceptions.AddRange (task.Exception.InnerExceptions);
continue;
}
if (task.IsCanceled) {
canceled = true;
}
}
if (exceptions != null) {
owner.TrySetException (new AggregateException (exceptions), false, false);
return;
}
if (canceled) {
owner.CancelReal ();
return;
}
owner.Finish ();
}
}
sealed class WhenAllContinuation<TResult> : IContinuation
{
readonly Task<TResult[]> owner;
readonly IList<Task<TResult>> tasks;
int counter;
public WhenAllContinuation (Task<TResult[]> owner, IList<Task<TResult>> tasks)
{
this.owner = owner;
this.counter = tasks.Count;
this.tasks = tasks;
}
public void Execute ()
{
if (Interlocked.Decrement (ref counter) != 0)
return;
bool canceled = false;
List<Exception> exceptions = null;
TResult[] results = null;
for (int i = 0; i < tasks.Count; ++i) {
var task = tasks [i];
if (task.IsFaulted) {
if (exceptions == null)
exceptions = new List<Exception> ();
exceptions.AddRange (task.Exception.InnerExceptions);
continue;
}
if (task.IsCanceled) {
canceled = true;
continue;
}
if (results == null) {
if (canceled || exceptions != null)
continue;
results = new TResult[tasks.Count];
}
results[i] = task.Result;
}
if (exceptions != null) {
owner.TrySetException (new AggregateException (exceptions), false, false);
return;
}
if (canceled) {
owner.CancelReal ();
return;
}
owner.TrySetResult (results);
}
}
sealed class WhenAnyContinuation<T> : IContinuation where T : Task
{
readonly Task<T> owner;
readonly IList<T> tasks;
AtomicBooleanValue executed;
public WhenAnyContinuation (Task<T> owner, IList<T> tasks)
{
this.owner = owner;
this.tasks = tasks;
executed = new AtomicBooleanValue ();
}
public void Execute ()
{
if (!executed.TryRelaxedSet ())
return;
bool owner_notified = false;
for (int i = 0; i < tasks.Count; ++i) {
var task = tasks[i];
if (!task.IsCompleted) {
task.RemoveContinuation (this);
continue;
}
if (owner_notified)
continue;
owner.TrySetResult (task);
owner_notified = true;
}
}
}
sealed class ManualResetContinuation : IContinuation, IDisposable
{
readonly ManualResetEventSlim evt;
public ManualResetContinuation ()
{
this.evt = new ManualResetEventSlim ();
}
public ManualResetEventSlim Event {
get {
return evt;
}
}
public void Dispose ()
{
evt.Dispose ();
}
public void Execute ()
{
evt.Set ();
}
}
sealed class CountdownContinuation : IContinuation, IDisposable
{
readonly CountdownEvent evt;
bool disposed;
public CountdownContinuation (int initialCount)
{
this.evt = new CountdownEvent (initialCount);
}
public CountdownEvent Event {
get {
return evt;
}
}
public void Dispose ()
{
disposed = true;
Thread.MemoryBarrier ();
evt.Dispose ();
}
public void Execute ()
{
// Guard against possible race when continuation is disposed and some tasks may still
// execute it (removal was late and the execution is slower than the Dispose thread)
if (!disposed)
evt.Signal ();
}
}
sealed class DisposeContinuation : IContinuation
{
readonly IDisposable instance;
public DisposeContinuation (IDisposable instance)
{
this.instance = instance;
}
public void Execute ()
{
instance.Dispose ();
}
}
}
#endif