PageAsyncTaskManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / UI / PageAsyncTaskManager.cs / 5 / PageAsyncTaskManager.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.UI { 
 
using System;
using System.Collections; 
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Web; 
using System.Web.UI;
using System.Web.Util; 
 
internal class PageAsyncTaskManager {
    private Page _page; 
    private HttpApplication _app;
    private HttpAsyncResult _asyncResult;
    private bool _failedToStart;
    private ArrayList _tasks; 
    private DateTime  _timeoutEnd;
    private volatile bool _timeoutEndReached; 
    private volatile bool _inProgress; 
    private int _tasksStarted;
    private int _tasksCompleted; 
    private WaitCallback _resumeTasksCallback;
    private Timer _timeoutTimer;

    internal PageAsyncTaskManager(Page page) { 
        _page = page;
        _app = page.Context.ApplicationInstance; 
        _tasks = new ArrayList(); 
        _resumeTasksCallback = new WaitCallback(this.ResumeTasksThreadpoolThread);
    } 

    internal HttpApplication Application {
        get { return _app; }
    } 

    internal void AddTask(PageAsyncTask task) { 
        _tasks.Add(task); 
    }
 
    internal bool AnyTasksRemain {
        get {
            for (int i = 0; i < _tasks.Count; i++) {
                PageAsyncTask task = (PageAsyncTask)_tasks[i]; 
                if (!task.Started) {
                    return true; 
                } 
            }
            return false; 
        }
    }

    internal bool FailedToStartTasks { 
        get { return _failedToStart; }
    } 
 
    internal bool TaskExecutionInProgress {
        get { return _inProgress; } 
    }

    private Exception AnyTaskError {
        get { 
            for (int i = 0; i < _tasks.Count; i++) {
                PageAsyncTask task = (PageAsyncTask)_tasks[i]; 
                if (task.Error != null) { 
                    return task.Error;
                } 
            }
            return null;
        }
    } 

    private bool TimeoutEndReached { 
        get { 
            if (!_timeoutEndReached && (DateTime.UtcNow >= _timeoutEnd)) {
                _timeoutEndReached = true; 
            }

            return _timeoutEndReached;
        } 
    }
 
    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++) { 
            PageAsyncTask task = (PageAsyncTask)_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 RegisterHandlersForPagePreRenderCompleteAsync() { 
        _page.AddOnPreRenderCompleteAsync( 
            new BeginEventHandler(this.BeginExecuteAsyncTasks),
            new EndEventHandler(this.EndExecuteAsyncTasks)); 
    }

    private IAsyncResult BeginExecuteAsyncTasks(object sender, EventArgs e, AsyncCallback cb, object extraData) {
        return ExecuteTasks(cb, extraData); 
    }
 
    private void EndExecuteAsyncTasks(IAsyncResult ar) { 
        _asyncResult.End();
    } 

    internal HttpAsyncResult ExecuteTasks(AsyncCallback callback, Object extraData) {
        _failedToStart = false;
        _timeoutEnd = DateTime.UtcNow + _page.AsyncTimeout; 
        _timeoutEndReached = false;
        _tasksStarted = 0; 
        _tasksCompleted = 0; 

        _asyncResult = new HttpAsyncResult(callback, extraData); 

        bool waitUntilDone = (callback == null);

        if (waitUntilDone) { 
            // when requested to wait for tasks, before starting tasks
            // make sure that the lock can be suspended. 
            try {} finally { 
                try {
                    Monitor.Exit(_app); 
                    Monitor.Enter(_app);
                }
                catch (SynchronizationLockException) {
                    _failedToStart = true; 
                    throw new InvalidOperationException(SR.GetString(SR.Async_tasks_wrong_thread));
                } 
            } 
        }
 
        _inProgress = true;

        try {
            // all work done here: 
            ResumeTasks(waitUntilDone, true /*onCallerThread*/);
        } 
        finally { 
            if (waitUntilDone) {
                _inProgress = false; 
            }
        }

        return _asyncResult; 
    }
 
    private void ResumeTasks(bool waitUntilDone, bool onCallerThread) { 

#if DBG 
        Debug.Trace("Async", "TaskManager.ResumeTasks: onCallerThread=" + onCallerThread +
            ", _tasksCompleted=" + _tasksCompleted + ", _tasksStarted=" + _tasksStarted);

        if (waitUntilDone) { 
            // must be on caller thread to wait
            Debug.Assert(onCallerThread); 
        } 
#endif
 
        // artifically increment the task count by one
        // to make sure _tasksCompleted doesn't become equal to _tasksStarted during this method
        Interlocked.Increment(ref _tasksStarted);
 
        try {
            if (onCallerThread) { 
                ResumeTasksPossiblyUnderLock(waitUntilDone); 
            }
            else { 
                lock (_app) {
                    HttpApplication.ThreadContext threadContext = null;
                    try {
                        threadContext = _app.OnThreadEnter(); 
                        ResumeTasksPossiblyUnderLock(waitUntilDone);
                    } 
                    finally { 
                        if (threadContext != null) {
                            threadContext.Leave(); 
                        }
                    }
                }
            } 
        }
        finally { 
            // complete the bogus task introduced with incrementing _tasksStarted at the beginning 
            TaskCompleted(onCallerThread);
        } 
    }

    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++) {
                PageAsyncTask task = (PageAsyncTask)_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 Monitor.Exit undoes the lock
                try {} finally { 
                    Monitor.Exit(_app);
                    locked = false; 
                } 

                WaitForAllStartedTasks(true /*syncCaller*/, false /*forceTimeout*/); 
            }
            finally {
                if (!locked) {
                    Monitor.Enter(_app); 
                }
            } 
        } 
    }
 
    private void ResumeTasksThreadpoolThread(Object data) {
        ResumeTasks(false /*waitUntilDone*/, false /*onCallerThread*/);
    }
 
    internal void TaskCompleted(bool onCallerThread) {
        int newTasksCompleted = Interlocked.Increment(ref _tasksCompleted); 
 
        Debug.Trace("Async", "TaskManager.TaskCompleted: onCallerThread=" + onCallerThread +
            ", _tasksCompleted=" + newTasksCompleted + ", _tasksStarted=" + _tasksStarted); 

        if (newTasksCompleted < _tasksStarted) {
            // need to wait for more completions
            return; 
        }
 
        // check if any tasks remain not started 
        if (!AnyTasksRemain) {
            // can complete the caller - all done 
            _inProgress = false;
            _asyncResult.Complete(onCallerThread, null /*result*/, AnyTaskError);
            return;
        } 

        // need to resume executing tasks 
        if (Thread.CurrentThread.IsThreadPoolThread) { 
            // if on thread pool thread, use the current thread
            ResumeTasks(false /*waitUntilDone*/, onCallerThread); 
        }
        else {
            // if on a non-threadpool thread, requeue
            ThreadPool.QueueUserWorkItem(_resumeTasksCallback); 
        }
    } 
 
    private void StartTimerIfNeeeded() {
        if (_timeoutTimer != null) { 
            return;
        }

        // calculate the wait time 
        DateTime utcNow = DateTime.UtcNow;
 
        if (utcNow >= _timeoutEnd) { 
            return;
        } 

        double timerPeriod = (_timeoutEnd - utcNow).TotalMilliseconds;

        if (timerPeriod >= (double)Int32.MaxValue) { 
            // timeout too big to launch timer (> ~25 days, plenty enough for an async page task)
            return; 
        } 

        // start the timer 
        Debug.Trace("Async", "Starting timeout timer for " + timerPeriod + " ms");
        _timeoutTimer = new Timer(new TimerCallback(this.TimeoutTimerCallback), null, (int)timerPeriod, -1);
    }
 
    internal void DisposeTimer() {
        Timer timer = _timeoutTimer; 
        if (timer != null && Interlocked.CompareExchange(ref _timeoutTimer, null, timer) == timer) { 
            timer.Dispose();
        } 
    }

    private void TimeoutTimerCallback(Object state) {
        DisposeTimer(); 

        // timeout everything that's left 
        WaitForAllStartedTasks(false /*syncCaller*/, false /*forceTimeout*/); 
    }
 
    internal void CompleteAllTasksNow() {
        WaitForAllStartedTasks(true /*syncCaller*/, true /*forceTimeout*/);
    }
} 

}
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK