Code:
/ FX-1434 / FX-1434 / 1.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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- PrintingPermission.cs
- DataGridViewCellValueEventArgs.cs
- SecurityDocument.cs
- CellTreeSimplifier.cs
- IndependentlyAnimatedPropertyMetadata.cs
- MultiBindingExpression.cs
- EntityAdapter.cs
- AttachmentCollection.cs
- EDesignUtil.cs
- Ref.cs
- SystemWebCachingSectionGroup.cs
- Compiler.cs
- CodeDomSerializerBase.cs
- DataGridViewHeaderCell.cs
- DesignerDataSourceView.cs
- FontStretchConverter.cs
- EntityViewGenerationAttribute.cs
- FixedHighlight.cs
- BinHexEncoder.cs
- SchemaElementDecl.cs
- OracleFactory.cs
- CalendarAutoFormatDialog.cs
- SchemaImporterExtensionElementCollection.cs
- PageFunction.cs
- JsonReaderDelegator.cs
- AffineTransform3D.cs
- Predicate.cs
- Vertex.cs
- OdbcFactory.cs
- XmlDocumentFragment.cs
- BrowserCapabilitiesCompiler.cs
- control.ime.cs
- BitmapCodecInfoInternal.cs
- HScrollProperties.cs
- Merger.cs
- TraceSection.cs
- SmtpFailedRecipientException.cs
- FtpWebRequest.cs
- ColumnHeaderConverter.cs
- WindowsScrollBar.cs
- AnimationException.cs
- MarkupCompilePass2.cs
- Pair.cs
- AccessibilityHelperForXpWin2k3.cs
- VirtualDirectoryMapping.cs
- SiteMapNodeCollection.cs
- PageAsyncTask.cs
- MetadataItem_Static.cs
- SplashScreenNativeMethods.cs
- PropertyAccessVisitor.cs
- DNS.cs
- EventLogEntry.cs
- LinkAreaEditor.cs
- ItemAutomationPeer.cs
- Point4D.cs
- SortAction.cs
- DataSourceControl.cs
- Variable.cs
- Propagator.ExtentPlaceholderCreator.cs
- _NTAuthentication.cs
- TreeWalkHelper.cs
- XNodeValidator.cs
- ReadingWritingEntityEventArgs.cs
- BulletedListDesigner.cs
- ListBase.cs
- HeaderUtility.cs
- CodeCatchClause.cs
- SoapReflectionImporter.cs
- Encoder.cs
- X509ChainPolicy.cs
- Int32.cs
- Vector.cs
- ViewStateModeByIdAttribute.cs
- Monitor.cs
- UnsafeNativeMethods.cs
- UpDownBase.cs
- WebResourceUtil.cs
- TableLayoutPanel.cs
- RelativeSource.cs
- StylusEditingBehavior.cs
- HealthMonitoringSectionHelper.cs
- DictionaryManager.cs
- NavigatorInput.cs
- DataStreamFromComStream.cs
- ScriptingAuthenticationServiceSection.cs
- CultureSpecificCharacterBufferRange.cs
- CompatibleComparer.cs
- isolationinterop.cs
- newinstructionaction.cs
- ListViewItemSelectionChangedEvent.cs
- ApplicationTrust.cs
- InstanceNameConverter.cs
- DescendantOverDescendantQuery.cs
- CellNormalizer.cs
- X509ThumbprintKeyIdentifierClause.cs
- InputLanguageManager.cs
- ExpandSegment.cs
- LinkUtilities.cs
- CharacterBuffer.cs
- SpecialNameAttribute.cs