//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//------------------------------------------------------------------------------
namespace System.Web.Util {
using System;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
// This class is used by the AspNetSynchronizationContext to assist with scheduling tasks in a non-blocking fashion.
// Asynchronous work will be queued and will execute sequentially, never consuming more than a single thread at a time.
// Synchronous work will block and will execute on the current thread.
internal sealed class SynchronizationHelper {
private Task _completionTask; // the Task that will run when all in-flight operations have completed
private Thread _currentThread; // the Thread that's running the current Task; all threads must see the same value for this field
private Task _lastScheduledTask = CreateInitialTask(); // the last Task that was queued to this helper, used to hook future Tasks (not volatile since always accessed under lock)
private Task _lastScheduledTaskAsync = CreateInitialTask(); // the last async Task that was queued to this helper
private readonly object _lockObj = new object(); // synchronizes access to _lastScheduledTask
private int _operationsInFlight; // operation counter
private readonly ISyncContext _syncContext; // a context that wraps an operation with pre- and post-execution phases
private readonly Action _appVerifierCallback; // for making sure that developers don't try calling us after the request has completed
public SynchronizationHelper(ISyncContext syncContext) {
_syncContext = syncContext;
_appVerifierCallback = AppVerifier.GetSyncContextCheckDelegate(syncContext);
}
// If an operation results in an exception, this property will provide access to it.
public ExceptionDispatchInfo Error { get; set; }
// Helper to access the _currentThread field in a thread-safe fashion.
// It is not enough to mark the _currentThread field volatile, since that only guarantees
// read / write ordering and doesn't ensure that each thread sees the same value.
private Thread CurrentThread {
get { return Interlocked.CompareExchange(ref _currentThread, null, null); }
set { Interlocked.Exchange(ref _currentThread, value); }
}
// Returns the number of pending operations
public int PendingCount { get { return ChangeOperationCount(0); } }
public int ChangeOperationCount(int addend) {
int newOperationCount = Interlocked.Add(ref _operationsInFlight, addend);
if (newOperationCount == 0) {
// if an asynchronous completion operation is queued, run it
Task completionTask = Interlocked.Exchange(ref _completionTask, null);
if (completionTask != null) {
completionTask.Start();
}
}
return newOperationCount;
}
private void CheckForRequestStateIfRequired(bool checkForReEntry) {
if (_appVerifierCallback != null) {
_appVerifierCallback(checkForReEntry);
}
}
// Creates the initial hook that future operations can ride off of
private static Task CreateInitialTask() {
return Task.FromResult