e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
192 lines
5.8 KiB
C#
192 lines
5.8 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="RequestTimeoutManager.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
/*
|
|
* Request timeout manager -- implements the request timeout mechanism
|
|
*/
|
|
namespace System.Web {
|
|
using System.Threading;
|
|
using System.Collections;
|
|
using System.Web.Util;
|
|
|
|
internal class RequestTimeoutManager {
|
|
private int _requestCount;
|
|
private DoubleLinkList[] _lists; // partitioned to avoid contention
|
|
private int _currentList;
|
|
private int _inProgressLock; // only 1 thread can be cancelling
|
|
private readonly TimeSpan _timerPeriod = new TimeSpan(0, 0, 15); // 15 second init precision
|
|
private Timer _timer;
|
|
|
|
internal RequestTimeoutManager() {
|
|
// initialize request lists
|
|
|
|
_requestCount = 0;
|
|
|
|
_lists = new DoubleLinkList[13];
|
|
for (int i = 0; i < _lists.Length; i++)
|
|
_lists[i] = new DoubleLinkList();
|
|
_currentList = 0;
|
|
|
|
// init lock
|
|
|
|
_inProgressLock = 0;
|
|
|
|
// create the timer
|
|
|
|
#if DBG
|
|
if (!Debug.IsTagPresent("Timer") || Debug.IsTagEnabled("Timer"))
|
|
#endif
|
|
{
|
|
_timer = new Timer(new TimerCallback(this.TimerCompletionCallback), null, _timerPeriod, _timerPeriod);
|
|
}
|
|
|
|
}
|
|
|
|
internal void Stop() {
|
|
// stop the timer
|
|
|
|
if (_timer != null) {
|
|
((IDisposable)_timer).Dispose();
|
|
_timer = null;
|
|
}
|
|
|
|
while (_inProgressLock != 0)
|
|
Thread.Sleep(100);
|
|
|
|
// cancel all cancelable requests
|
|
|
|
if (_requestCount > 0)
|
|
CancelTimedOutRequests(DateTime.UtcNow.AddYears(1)); // future date
|
|
}
|
|
|
|
private void TimerCompletionCallback(Object state) {
|
|
if (_requestCount > 0)
|
|
CancelTimedOutRequests(DateTime.UtcNow);
|
|
}
|
|
|
|
private void CancelTimedOutRequests(DateTime now) {
|
|
|
|
// only one thread can be doing it
|
|
|
|
if (Interlocked.CompareExchange(ref _inProgressLock, 1, 0) != 0)
|
|
return;
|
|
|
|
// collect them all into a separate list with minimal locking
|
|
|
|
ArrayList entries = new ArrayList(_requestCount); // size can change
|
|
DoubleLinkListEnumerator en;
|
|
|
|
for (int i = 0; i < _lists.Length; i++) {
|
|
lock (_lists[i]) {
|
|
en = _lists[i].GetEnumerator();
|
|
|
|
while (en.MoveNext())
|
|
entries.Add(en.GetDoubleLink());
|
|
|
|
en = null;
|
|
}
|
|
}
|
|
|
|
// walk through the collected list to timeout what's needed
|
|
|
|
int n = entries.Count;
|
|
|
|
for (int i = 0; i < n; i++)
|
|
((RequestTimeoutEntry)entries[i]).TimeoutIfNeeded(now);
|
|
|
|
// this thread is done -- unlock
|
|
|
|
Interlocked.Exchange(ref _inProgressLock, 0);
|
|
}
|
|
|
|
internal void Add(HttpContext context) {
|
|
if (context.TimeoutLink != null) {
|
|
((RequestTimeoutEntry)context.TimeoutLink).IncrementCount();
|
|
return;
|
|
}
|
|
|
|
// create new entry
|
|
|
|
RequestTimeoutEntry entry = new RequestTimeoutEntry(context);
|
|
|
|
// add it to the list
|
|
|
|
int i = _currentList++;
|
|
if (i >= _lists.Length) {
|
|
i = 0;
|
|
_currentList = 0;
|
|
}
|
|
|
|
entry.AddToList(_lists[i]);
|
|
Interlocked.Increment(ref _requestCount);
|
|
|
|
// update HttpContext
|
|
context.TimeoutLink = entry;
|
|
}
|
|
|
|
internal void Remove(HttpContext context) {
|
|
RequestTimeoutEntry entry = (RequestTimeoutEntry)context.TimeoutLink;
|
|
|
|
// remove from the list
|
|
if (entry != null) {
|
|
if( entry.DecrementCount() == 0 ) {
|
|
entry.RemoveFromList();
|
|
Interlocked.Decrement(ref _requestCount);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// update HttpContext
|
|
context.TimeoutLink = null;
|
|
}
|
|
|
|
private class RequestTimeoutEntry : DoubleLink {
|
|
private HttpContext _context; // the request
|
|
private DoubleLinkList _list;
|
|
private int _count;
|
|
|
|
internal RequestTimeoutEntry(HttpContext context) {
|
|
_context = context;
|
|
_count = 1;
|
|
}
|
|
|
|
internal void AddToList(DoubleLinkList list) {
|
|
lock(list) {
|
|
list.InsertTail(this);
|
|
_list = list;
|
|
}
|
|
}
|
|
|
|
internal void RemoveFromList() {
|
|
if (_list != null) {
|
|
lock(_list) {
|
|
Remove();
|
|
_list = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void TimeoutIfNeeded(DateTime now) {
|
|
Thread thread = _context.MustTimeout(now);
|
|
if (thread != null) {
|
|
RemoveFromList();
|
|
thread.Abort(new HttpApplication.CancelModuleException(true));
|
|
}
|
|
}
|
|
|
|
internal void IncrementCount() {
|
|
Interlocked.Increment( ref _count );
|
|
}
|
|
|
|
internal int DecrementCount() {
|
|
return Interlocked.Decrement( ref _count );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|