private void WaitForAllStartedTasks(bool syncCaller, bool forceTimeout)
        {
            // don't foreach because the ArrayList could be modified by tasks' end methods
            for (int i = 0; i < _tasks.Count; i++)
            {
                LegacyPageAsyncTask task = (LegacyPageAsyncTask)_tasks[i];

                if (!task.Started || task.Completed)
                {
                    continue;
                }

                // need to wait, but no longer than timeout.
                if (!forceTimeout && !TimeoutEndReached)
                {
                    DateTime utcNow = DateTime.UtcNow;

                    if (utcNow < _timeoutEnd) // re-check not to wait negative time span
                    {
                        WaitHandle waitHandle = task.AsyncResult.AsyncWaitHandle;

                        if (waitHandle != null)
                        {
                            bool signaled = waitHandle.WaitOne(_timeoutEnd - utcNow, false);

                            if (signaled && task.Completed)
                            {
                                // a task could complete before timeout expires
                                // in this case go to the next task
                                continue;
                            }
                        }
                    }
                }

                // start polling after timeout reached (or if there is no handle to wait on)

                bool taskTimeoutForced = false;

                while (!task.Completed)
                {
                    if (forceTimeout || (!taskTimeoutForced && TimeoutEndReached))
                    {
                        task.ForceTimeout(syncCaller);
                        taskTimeoutForced = true;
                    }
                    else
                    {
                        Thread.Sleep(50);
                    }
                }
            }
        }
 internal void AddTask(LegacyPageAsyncTask task) {
     _tasks.Add(task);
 }
 internal void AddTask(LegacyPageAsyncTask task)
 {
     _tasks.Add(task);
 }
        private void ResumeTasksPossiblyUnderLock(bool waitUntilDone)
        {
            while (AnyTasksRemain)
            {
                bool someTasksStarted     = false;
                bool realAsyncTaskStarted = false;
                bool parallelTaskStarted  = false;

                // start the tasks

                for (int i = 0; i < _tasks.Count; i++)
                {
                    LegacyPageAsyncTask task = (LegacyPageAsyncTask)_tasks[i];

                    if (task.Started)
                    {
                        continue; // ignore already started tasks
                    }

                    if (parallelTaskStarted && !task.ExecuteInParallel)
                    {
                        // already started a parallel task, so need to ignore sequential ones
                        continue;
                    }

                    someTasksStarted = true;
                    Interlocked.Increment(ref _tasksStarted);

                    task.Start(this, _page, EventArgs.Empty);

                    if (task.CompletedSynchronously)
                    {
                        continue; // ignore the ones completed synchornously
                    }

                    // at this point a truly async task has been started
                    realAsyncTaskStarted = true;

                    if (task.ExecuteInParallel)
                    {
                        parallelTaskStarted = true;
                    }
                    else
                    {
                        // only one sequential task at a time
                        break;
                    }
                }

                if (!someTasksStarted)
                {
                    // no tasks to start, all done
                    break;
                }

                if (!TimeoutEndReached && realAsyncTaskStarted && !waitUntilDone)
                {
                    // make sure we have a timer going
                    StartTimerIfNeeeded();

                    // unwind the stack for async callers
                    break;
                }

                // need to wait until tasks comlete, but the wait
                // must be outside of the lock (deadlock otherwise)

                // this code is always already under lock
                bool locked = true;

                try {
                    // outer code has lock(_app) { ... }
                    // the assumption here is that Disassociate undoes the lock
                    try {} finally {
                        _app.Context.SyncContext.DisassociateFromCurrentThread();
                        locked = false;
                    }

                    WaitForAllStartedTasks(true /*syncCaller*/, false /*forceTimeout*/);
                }
                finally {
                    if (!locked)
                    {
                        _app.Context.SyncContext.AssociateWithCurrentThread();
                    }
                }
            }
        }