Merge pull request #799 from kebby/master
authorMarek Safar <marek.safar@gmail.com>
Tue, 12 Nov 2013 08:38:48 +0000 (00:38 -0800)
committerMarek Safar <marek.safar@gmail.com>
Tue, 12 Nov 2013 08:38:48 +0000 (00:38 -0800)
Task.WhenAllCore<T> now handles empty argument list correctly. Fixes #15956

1  2 
mcs/class/corlib/System.Threading.Tasks/Task.cs
mcs/class/corlib/Test/System.Threading.Tasks/TaskTest.cs

index 1ae5559957ee710ce4206daf00fb0532f1df6987,d2a108064a5051547c678e74239d67dfe0caa7e2..5eefcb7da5f87592bc67d2751e579f0b95ed1055
@@@ -6,7 -6,7 +6,7 @@@
  //    Jérémie Laval <jeremie dot laval at xamarin dot com>
  //
  // Copyright (c) 2008 Jérémie "Garuma" Laval
 -// Copyright 2011 Xamarin Inc (http://www.xamarin.com).
 +// Copyright 2011-2013 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
@@@ -46,11 -46,13 +46,11 @@@ namespace System.Threading.Task
                // and for Parent property.
                [System.ThreadStatic]
                static Task current;
 -              [System.ThreadStatic]
 -              static Action<Task> childWorkAdder;
                
                // parent is the outer task in which this task is created
                readonly Task parent;
 -              // contAncestor is the Task on which this continuation was setup
 -              readonly Task contAncestor;
 +              // A reference to a Task on which this continuation is attached to
 +              Task contAncestor;
                
                static int          id = -1;
                static readonly TaskFactory defaultFactory = new TaskFactory ();
@@@ -63,7 -65,6 +63,7 @@@
                internal TaskScheduler       scheduler;
  
                TaskExceptionSlot exSlot;
 +              ManualResetEvent wait_handle;
  
                TaskStatus          status;
  
                internal void RunSynchronouslyCore (TaskScheduler scheduler)
                {
                        SetupScheduler (scheduler);
 -                      var saveStatus = status;
                        Status = TaskStatus.WaitingToRun;
  
                        try {
                                throw new TaskSchedulerException (inner);
                        }
  
 -                      Status = saveStatus;
 -                      Start (scheduler);
 +                      Schedule ();
                        Wait ();
                }
                #endregion
                internal void Schedule ()
                {
                        Status = TaskStatus.WaitingToRun;
 -                      
 -                      // If worker is null it means it is a local one, revert to the old behavior
 -                      // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
 -                      if (scheduler != TaskScheduler.Current || childWorkAdder == null || HasFlag (creationOptions, TaskCreationOptions.PreferFairness)) {
 -                              scheduler.QueueTask (this);
 -                      } else {
 -                              /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom 
 -                               * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
 -                               * the correct Thread during the creation
 -                               */
 -                              childWorkAdder (this);
 -                      }
 +                      scheduler.QueueTask (this);
                }
                
                void ThreadStart ()
                void InnerInvoke ()
                {
                        if (IsContinuation) {
 -                              invoker.Invoke (contAncestor, state, this);
 +                              var ancestor = contAncestor;
 +                              contAncestor = null;
 +                              invoker.Invoke (ancestor, state, this);
                        } else {
                                invoker.Invoke (this, state, this);
                        }
                                        Status = TaskStatus.WaitingForChildrenToComplete;
                        }
  
 +                      if (wait_handle != null)
 +                              wait_handle.Set ();
 +
                        // Tell parent that we are finished
                        if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) &&
  #if NET_4_5
                internal void CancelReal ()
                {
                        Status = TaskStatus.Canceled;
 +
 +                      if (wait_handle != null)
 +                              wait_handle.Set ();
 +
                        ProcessCompleteDelegates ();
                }
  
                        ExceptionSlot.Exception = e;
                        Thread.MemoryBarrier ();
                        Status = TaskStatus.Faulted;
 +
 +                      if (wait_handle != null)
 +                              wait_handle.Set ();
 +
                        ProcessCompleteDelegates ();
                }
  
                        if (millisecondsTimeout < -1)
                                throw new ArgumentOutOfRangeException ("millisecondsTimeout");
  
 -                      bool result = true;
 -
 -                      if (!IsCompleted) {
 -                              // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
 -                              if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
 -                                      scheduler.RunInline (this, true);
 -
 -                              if (!IsCompleted) {
 -                                      var continuation = new ManualResetContinuation ();
 -                                      try {
 -                                              ContinueWith (continuation);
 -                                              result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
 -                                      } finally {
 -                                              if (!result)
 -                                                      RemoveContinuation (continuation);
 -                                              continuation.Dispose ();
 -                                      }
 -                              }
 -                      }
 +                      bool result = WaitCore (millisecondsTimeout, cancellationToken);
  
                        if (IsCanceled)
                                throw new AggregateException (new TaskCanceledException (this));
  
                        return result;
                }
 +
 +              internal bool WaitCore (int millisecondsTimeout, CancellationToken cancellationToken)
 +              {
 +                      if (IsCompleted)
 +                              return true;
 +
 +                      // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
 +                      if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
 +                              scheduler.RunInline (this, true);
 +
 +                      bool result = true;
 +
 +                      if (!IsCompleted) {
 +                              var continuation = new ManualResetContinuation ();
 +                              try {
 +                                      ContinueWith (continuation);
 +                                      result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
 +                              } finally {
 +                                      if (!result)
 +                                              RemoveContinuation (continuation);
 +                                      continuation.Dispose ();
 +                              }
 +                      }
 +
 +                      return result;
 +              }
                
                public static void WaitAll (params Task[] tasks)
                {
                #region Dispose
                public void Dispose ()
                {
 -                      Dispose (true);
 +                      Dispose (true);                 
                }
                
                protected virtual void Dispose (bool disposing)
                                state = null;
                                if (cancellationRegistration != null)
                                        cancellationRegistration.Value.Dispose ();
 +                              if (wait_handle != null)
 +                                      wait_handle.Dispose ();
                        }
                }
                #endregion
                        if (millisecondsDelay < -1)
                                throw new ArgumentOutOfRangeException ("millisecondsDelay");
  
 -                      var task = new Task (TaskActionInvoker.Delay, millisecondsDelay, cancellationToken, TaskCreationOptions.None, null, TaskConstants.Finished);
 -                      task.SetupScheduler (TaskScheduler.Current);
 -                      
 -                      if (millisecondsDelay != Timeout.Infinite)
 -                              task.scheduler.QueueTask (task);
 +                      if (cancellationToken.IsCancellationRequested)
 +                              return TaskConstants.Canceled;
 +
 +                      var task = new Task (TaskActionInvoker.Empty, null, cancellationToken, TaskCreationOptions.None, null, null);
 +                      task.SetupScheduler (TaskScheduler.Default);
 +
 +                      if (millisecondsDelay != Timeout.Infinite) {
 +                              var timer = new Timer (delegate (object state) {
 +                                      var t = (Task) state;
 +                                      if (t.Status == TaskStatus.WaitingForActivation) {
 +                                              t.Status = TaskStatus.Running;
 +                                              t.Finish ();
 +                                      }
 +                              }, task, millisecondsDelay, -1);
 +
 +                              task.ContinueWith (new DisposeContinuation (timer));
 +                      }
  
                        return task;
                }
  
                internal static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
                {
+                       if (tasks.Count == 0)
+                               return FromResult(new TResult[0]);
                        foreach (var t in tasks) {
                                if (t == null)
                                        throw new ArgumentException ("tasks", "the tasks argument contains a null element");
  
                WaitHandle IAsyncResult.AsyncWaitHandle {
                        get {
 -                              return null;
 +                              if (invoker == null)
 +                                      throw new ObjectDisposedException (GetType ().ToString ());
 +
 +                              if (wait_handle == null)
 +                                      Interlocked.CompareExchange (ref wait_handle, new ManualResetEvent (IsCompleted), null);
 +
 +                              return wait_handle;
                        }
                }
                
index feff0e8b06f234afa871dfb8c18570b201ba566a,c6f9c5b222dab158daae6d010c9eca0d83d4439e..f61a64d4aa0f89fceb29833e418b9910cafabce2
@@@ -67,25 -67,6 +67,25 @@@ namespace MonoTests.System.Threading.Ta
                        }
                }
  
 +              class NonInlineableScheduler : TaskScheduler
 +              {
 +                      protected override IEnumerable<Task> GetScheduledTasks ()
 +                      {
 +                              throw new NotImplementedException ();
 +                      }
 +
 +                      protected override void QueueTask (Task task)
 +                      {
 +                              if (!base.TryExecuteTask (task))
 +                                      throw new ApplicationException ();
 +                      }
 +
 +                      protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
 +                      {
 +                              return false;
 +                      }
 +              }
 +
  
                Task[] tasks;
                const int max = 6;
                        Assert.IsTrue (r2);
                }
  
 +              [Test]
 +              public void AsyncWaitHandleSet ()
 +              {
 +                      var task = new TaskFactory ().StartNew (() => { });
 +                      var ar = (IAsyncResult)task;
 +                      ar.AsyncWaitHandle.WaitOne ();
 +              }
 +
  #if NET_4_5
                [Test]
                public void Delay_Invalid ()
                        }
                }
  
 +              [Test]
 +              public void Delay_TimeManagement ()
 +              {
 +                      var delay1 = Task.Delay(50);
 +                      var delay2 = Task.Delay(25);
 +                      Assert.IsTrue (Task.WhenAny(new[] { delay1, delay2 }).Wait (1000));
 +                      Assert.AreEqual (TaskStatus.RanToCompletion, delay2.Status);
 +              }
 +
                [Test]
                public void WaitAny_WithNull ()
                {
                        }
                }
  
+               [Test]
+               public void WhenAll_Empty ()
+               {
+                       var tasks = new Task[0];
+                       Task t = Task.WhenAll(tasks);
+                       Assert.IsTrue(t.Wait(1000), "#1");
+               }
                [Test]
                public void WhenAll_WithNull ()
                {
                        Assert.IsTrue (t.Wait (1000), "#2");
                }
  
+               [Test]
+               public void WhenAllResult_Empty ()
+               {
+                       var tasks = new Task<int>[0];
+                       Task<int[]> t = Task.WhenAll(tasks);
+                       Assert.IsTrue(t.Wait(1000), "#1");
+                       Assert.IsNotNull(t.Result, "#2");
+                       Assert.AreEqual(t.Result.Length, 0, "#3");
+               }
                [Test]
                public void WhenAllResult_WithNull ()
                {
                        Assert.AreEqual ('d', d.Result, "#3r");
                }
  
 +              [Test]
 +              public void ContinueWith_CustomScheduleRejected ()
 +              {
 +                      var scheduler = new NonInlineableScheduler ();
 +                      var t = Task.Factory.StartNew (delegate { }).
 +                              ContinueWith (r => {}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler);
 +                      
 +                      Assert.IsTrue (t.Wait (5000));
 +              }
 +
                [Test]
                public void FromResult ()
                {