Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / UI / PageAsyncTaskManager.cs / 1305376 / 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);
#pragma warning disable 0618
//@
Monitor.Enter(_app);
#pragma warning restore 0618
}
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) {
#pragma warning disable 0618
//@
Monitor.Enter(_app);
#pragma warning restore 0618
}
}
}
}
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(bool syncCaller) {
WaitForAllStartedTasks(syncCaller, true /*forceTimeout*/);
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- InternalResources.cs
- HandledEventArgs.cs
- BindingNavigator.cs
- XmlValueConverter.cs
- HttpChannelHelper.cs
- ConnectionProviderAttribute.cs
- FillBehavior.cs
- CompilationUtil.cs
- ComponentManagerBroker.cs
- InputProcessorProfiles.cs
- SafeRegistryHandle.cs
- GenericsNotImplementedException.cs
- ClientUtils.cs
- ListBindingConverter.cs
- WebServiceErrorEvent.cs
- propertyentry.cs
- DataGridToolTip.cs
- FrameworkRichTextComposition.cs
- OracleCommand.cs
- ProcessHost.cs
- Section.cs
- AvtEvent.cs
- CorrelationTokenInvalidatedHandler.cs
- StylusPointPropertyInfo.cs
- ResXDataNode.cs
- DoubleLink.cs
- SQLDecimal.cs
- XamlTypeMapperSchemaContext.cs
- TableRowGroup.cs
- NoClickablePointException.cs
- ValueOfAction.cs
- CharEnumerator.cs
- NameGenerator.cs
- InfoCardSymmetricAlgorithm.cs
- WebPartPersonalization.cs
- GiveFeedbackEventArgs.cs
- HwndProxyElementProvider.cs
- KeyInterop.cs
- DefaultPrintController.cs
- TableCellCollection.cs
- WindowPattern.cs
- Context.cs
- TypedTableBase.cs
- OracleInfoMessageEventArgs.cs
- SimpleFieldTemplateFactory.cs
- DirectionalLight.cs
- FastPropertyAccessor.cs
- ToolStripCodeDomSerializer.cs
- ObjectStateFormatter.cs
- TextCharacters.cs
- Error.cs
- Validator.cs
- XmlLanguageConverter.cs
- CurrencyWrapper.cs
- OutgoingWebResponseContext.cs
- QilVisitor.cs
- DataGridCaption.cs
- AsyncCompletedEventArgs.cs
- OutputScopeManager.cs
- TriggerActionCollection.cs
- NameValueCollection.cs
- ScalarRestriction.cs
- Container.cs
- ValidatorCompatibilityHelper.cs
- PageBreakRecord.cs
- ClientSideProviderDescription.cs
- TransportSecurityProtocol.cs
- ConstNode.cs
- XmlDocumentFragment.cs
- Stroke2.cs
- TransportReplyChannelAcceptor.cs
- StateChangeEvent.cs
- IdentityNotMappedException.cs
- Parameter.cs
- AssemblyName.cs
- dbdatarecord.cs
- ColorAnimation.cs
- SqlComparer.cs
- ServiceModelConfigurationSectionCollection.cs
- Identity.cs
- Menu.cs
- SynchronizedInputAdaptor.cs
- ProcessHostFactoryHelper.cs
- MsmqNonTransactedPoisonHandler.cs
- AnnotationResourceChangedEventArgs.cs
- TextWriterEngine.cs
- SurrogateChar.cs
- InheritanceContextHelper.cs
- PropertyNames.cs
- SwitchLevelAttribute.cs
- VirtualPathProvider.cs
- DataGridViewAutoSizeColumnModeEventArgs.cs
- XmlIlGenerator.cs
- SafePEFileHandle.cs
- DataGridCellEditEndingEventArgs.cs
- DbReferenceCollection.cs
- UTF7Encoding.cs
- DataServices.cs
- ComponentConverter.cs
- SymbolType.cs