457 lines
14 KiB
C#
457 lines
14 KiB
C#
|
//-----------------------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.ServiceModel.Channels
|
||
|
{
|
||
|
using System.Runtime;
|
||
|
using System.Threading;
|
||
|
|
||
|
enum LifetimeState
|
||
|
{
|
||
|
Opened,
|
||
|
Closing,
|
||
|
Closed
|
||
|
}
|
||
|
|
||
|
class LifetimeManager
|
||
|
{
|
||
|
#if DEBUG_EXPENSIVE
|
||
|
StackTrace closeStack;
|
||
|
#endif
|
||
|
bool aborted;
|
||
|
int busyCount;
|
||
|
ICommunicationWaiter busyWaiter;
|
||
|
int busyWaiterCount;
|
||
|
object mutex;
|
||
|
LifetimeState state;
|
||
|
|
||
|
public LifetimeManager(object mutex)
|
||
|
{
|
||
|
this.mutex = mutex;
|
||
|
this.state = LifetimeState.Opened;
|
||
|
}
|
||
|
|
||
|
public int BusyCount
|
||
|
{
|
||
|
get { return this.busyCount; }
|
||
|
}
|
||
|
|
||
|
protected LifetimeState State
|
||
|
{
|
||
|
get { return this.state; }
|
||
|
}
|
||
|
|
||
|
protected object ThisLock
|
||
|
{
|
||
|
get { return this.mutex; }
|
||
|
}
|
||
|
|
||
|
public void Abort()
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.State == LifetimeState.Closed || this.aborted)
|
||
|
return;
|
||
|
#if DEBUG_EXPENSIVE
|
||
|
if (closeStack == null)
|
||
|
closeStack = new StackTrace();
|
||
|
#endif
|
||
|
this.aborted = true;
|
||
|
this.state = LifetimeState.Closing;
|
||
|
}
|
||
|
|
||
|
this.OnAbort();
|
||
|
this.state = LifetimeState.Closed;
|
||
|
}
|
||
|
|
||
|
void ThrowIfNotOpened()
|
||
|
{
|
||
|
if (!this.aborted && this.state != LifetimeState.Opened)
|
||
|
{
|
||
|
#if DEBUG_EXPENSIVE
|
||
|
String originalStack = closeStack.ToString().Replace("\r\n", "\r\n ");
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString() + ", Object already closed:\r\n " + originalStack));
|
||
|
#else
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString()));
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
this.ThrowIfNotOpened();
|
||
|
#if DEBUG_EXPENSIVE
|
||
|
if (closeStack == null)
|
||
|
closeStack = new StackTrace();
|
||
|
#endif
|
||
|
this.state = LifetimeState.Closing;
|
||
|
}
|
||
|
|
||
|
return this.OnBeginClose(timeout, callback, state);
|
||
|
}
|
||
|
|
||
|
public void Close(TimeSpan timeout)
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
this.ThrowIfNotOpened();
|
||
|
#if DEBUG_EXPENSIVE
|
||
|
if (closeStack == null)
|
||
|
closeStack = new StackTrace();
|
||
|
#endif
|
||
|
this.state = LifetimeState.Closing;
|
||
|
}
|
||
|
|
||
|
this.OnClose(timeout);
|
||
|
this.state = LifetimeState.Closed;
|
||
|
}
|
||
|
|
||
|
CommunicationWaitResult CloseCore(TimeSpan timeout, bool aborting)
|
||
|
{
|
||
|
ICommunicationWaiter busyWaiter = null;
|
||
|
CommunicationWaitResult result = CommunicationWaitResult.Succeeded;
|
||
|
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.busyCount > 0)
|
||
|
{
|
||
|
if (this.busyWaiter != null)
|
||
|
{
|
||
|
if (!aborting && this.aborted)
|
||
|
return CommunicationWaitResult.Aborted;
|
||
|
busyWaiter = this.busyWaiter;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
busyWaiter = new SyncCommunicationWaiter(this.ThisLock);
|
||
|
this.busyWaiter = busyWaiter;
|
||
|
}
|
||
|
Interlocked.Increment(ref busyWaiterCount);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (busyWaiter != null)
|
||
|
{
|
||
|
result = busyWaiter.Wait(timeout, aborting);
|
||
|
if (Interlocked.Decrement(ref busyWaiterCount) == 0)
|
||
|
{
|
||
|
busyWaiter.Dispose();
|
||
|
this.busyWaiter = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
protected void DecrementBusyCount()
|
||
|
{
|
||
|
ICommunicationWaiter busyWaiter = null;
|
||
|
bool empty = false;
|
||
|
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.busyCount <= 0)
|
||
|
{
|
||
|
throw Fx.AssertAndThrow("LifetimeManager.DecrementBusyCount: (this.busyCount > 0)");
|
||
|
}
|
||
|
if (--this.busyCount == 0)
|
||
|
{
|
||
|
if (this.busyWaiter != null)
|
||
|
{
|
||
|
busyWaiter = this.busyWaiter;
|
||
|
Interlocked.Increment(ref this.busyWaiterCount);
|
||
|
}
|
||
|
empty = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (busyWaiter != null)
|
||
|
{
|
||
|
busyWaiter.Signal();
|
||
|
if (Interlocked.Decrement(ref this.busyWaiterCount) == 0)
|
||
|
{
|
||
|
busyWaiter.Dispose();
|
||
|
this.busyWaiter = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (empty && this.State == LifetimeState.Opened)
|
||
|
OnEmpty();
|
||
|
}
|
||
|
|
||
|
public void EndClose(IAsyncResult result)
|
||
|
{
|
||
|
this.OnEndClose(result);
|
||
|
this.state = LifetimeState.Closed;
|
||
|
}
|
||
|
|
||
|
protected virtual void IncrementBusyCount()
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
Fx.Assert(this.State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCount: (this.State == LifetimeState.Opened)");
|
||
|
this.busyCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected virtual void IncrementBusyCountWithoutLock()
|
||
|
{
|
||
|
Fx.Assert(this.State == LifetimeState.Opened, "LifetimeManager.IncrementBusyCountWithoutLock: (this.State == LifetimeState.Opened)");
|
||
|
this.busyCount++;
|
||
|
}
|
||
|
|
||
|
protected virtual void OnAbort()
|
||
|
{
|
||
|
// We have decided not to make this configurable
|
||
|
CloseCore(TimeSpan.FromSeconds(1), true);
|
||
|
}
|
||
|
|
||
|
protected virtual IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
|
||
|
{
|
||
|
CloseCommunicationAsyncResult closeResult = null;
|
||
|
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.busyCount > 0)
|
||
|
{
|
||
|
if (this.busyWaiter != null)
|
||
|
{
|
||
|
Fx.Assert(this.aborted, "LifetimeManager.OnBeginClose: (this.aborted == true)");
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString()));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
closeResult = new CloseCommunicationAsyncResult(timeout, callback, state, this.ThisLock);
|
||
|
Fx.Assert(this.busyWaiter == null, "LifetimeManager.OnBeginClose: (this.busyWaiter == null)");
|
||
|
this.busyWaiter = closeResult;
|
||
|
Interlocked.Increment(ref this.busyWaiterCount);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (closeResult != null)
|
||
|
{
|
||
|
return closeResult;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return new CompletedAsyncResult(callback, state);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected virtual void OnClose(TimeSpan timeout)
|
||
|
{
|
||
|
switch (CloseCore(timeout, false))
|
||
|
{
|
||
|
case CommunicationWaitResult.Expired:
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.SFxCloseTimedOut1, timeout)));
|
||
|
case CommunicationWaitResult.Aborted:
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected virtual void OnEmpty()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected virtual void OnEndClose(IAsyncResult result)
|
||
|
{
|
||
|
if (result is CloseCommunicationAsyncResult)
|
||
|
{
|
||
|
CloseCommunicationAsyncResult.End(result);
|
||
|
if (Interlocked.Decrement(ref this.busyWaiterCount) == 0)
|
||
|
{
|
||
|
this.busyWaiter.Dispose();
|
||
|
this.busyWaiter = null;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
CompletedAsyncResult.End(result);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
enum CommunicationWaitResult
|
||
|
{
|
||
|
Waiting,
|
||
|
Succeeded,
|
||
|
Expired,
|
||
|
Aborted
|
||
|
}
|
||
|
|
||
|
interface ICommunicationWaiter : IDisposable
|
||
|
{
|
||
|
void Signal();
|
||
|
CommunicationWaitResult Wait(TimeSpan timeout, bool aborting);
|
||
|
}
|
||
|
|
||
|
class CloseCommunicationAsyncResult : AsyncResult, ICommunicationWaiter
|
||
|
{
|
||
|
object mutex;
|
||
|
CommunicationWaitResult result;
|
||
|
IOThreadTimer timer;
|
||
|
TimeoutHelper timeoutHelper;
|
||
|
TimeSpan timeout;
|
||
|
|
||
|
public CloseCommunicationAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, object mutex)
|
||
|
: base(callback, state)
|
||
|
{
|
||
|
this.timeout = timeout;
|
||
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
||
|
this.mutex = mutex;
|
||
|
|
||
|
if (timeout < TimeSpan.Zero)
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.SFxCloseTimedOut1, timeout)));
|
||
|
|
||
|
this.timer = new IOThreadTimer(new Action<object>(TimeoutCallback), this, true);
|
||
|
this.timer.Set(timeout);
|
||
|
}
|
||
|
|
||
|
object ThisLock
|
||
|
{
|
||
|
get { return mutex; }
|
||
|
}
|
||
|
|
||
|
public void Dispose()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
public static void End(IAsyncResult result)
|
||
|
{
|
||
|
AsyncResult.End<CloseCommunicationAsyncResult>(result);
|
||
|
}
|
||
|
|
||
|
public void Signal()
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.result != CommunicationWaitResult.Waiting)
|
||
|
return;
|
||
|
this.result = CommunicationWaitResult.Succeeded;
|
||
|
}
|
||
|
this.timer.Cancel();
|
||
|
this.Complete(false);
|
||
|
}
|
||
|
|
||
|
void Timeout()
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.result != CommunicationWaitResult.Waiting)
|
||
|
return;
|
||
|
this.result = CommunicationWaitResult.Expired;
|
||
|
}
|
||
|
this.Complete(false, new TimeoutException(SR.GetString(SR.SFxCloseTimedOut1, this.timeout)));
|
||
|
}
|
||
|
|
||
|
static void TimeoutCallback(object state)
|
||
|
{
|
||
|
CloseCommunicationAsyncResult closeResult = (CloseCommunicationAsyncResult)state;
|
||
|
closeResult.Timeout();
|
||
|
}
|
||
|
|
||
|
public CommunicationWaitResult Wait(TimeSpan timeout, bool aborting)
|
||
|
{
|
||
|
if (timeout < TimeSpan.Zero)
|
||
|
{
|
||
|
return CommunicationWaitResult.Expired;
|
||
|
}
|
||
|
|
||
|
// Synchronous Wait on AsyncResult should only be called in Abort code-path
|
||
|
Fx.Assert(aborting, "CloseCommunicationAsyncResult.Wait: (aborting == true)");
|
||
|
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.result != CommunicationWaitResult.Waiting)
|
||
|
{
|
||
|
return this.result;
|
||
|
}
|
||
|
this.result = CommunicationWaitResult.Aborted;
|
||
|
}
|
||
|
this.timer.Cancel();
|
||
|
|
||
|
TimeoutHelper.WaitOne(this.AsyncWaitHandle, timeout);
|
||
|
|
||
|
this.Complete(false, new ObjectDisposedException(this.GetType().ToString()));
|
||
|
return this.result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal class SyncCommunicationWaiter : ICommunicationWaiter
|
||
|
{
|
||
|
bool closed;
|
||
|
object mutex;
|
||
|
CommunicationWaitResult result;
|
||
|
ManualResetEvent waitHandle;
|
||
|
|
||
|
public SyncCommunicationWaiter(object mutex)
|
||
|
{
|
||
|
this.mutex = mutex;
|
||
|
this.waitHandle = new ManualResetEvent(false);
|
||
|
}
|
||
|
|
||
|
object ThisLock
|
||
|
{
|
||
|
get { return this.mutex; }
|
||
|
}
|
||
|
|
||
|
public void Dispose()
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.closed)
|
||
|
return;
|
||
|
this.closed = true;
|
||
|
this.waitHandle.Close();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Signal()
|
||
|
{
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.closed)
|
||
|
return;
|
||
|
this.waitHandle.Set();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public CommunicationWaitResult Wait(TimeSpan timeout, bool aborting)
|
||
|
{
|
||
|
if (this.closed)
|
||
|
{
|
||
|
return CommunicationWaitResult.Aborted;
|
||
|
}
|
||
|
if (timeout < TimeSpan.Zero)
|
||
|
{
|
||
|
return CommunicationWaitResult.Expired;
|
||
|
}
|
||
|
|
||
|
if (aborting)
|
||
|
{
|
||
|
this.result = CommunicationWaitResult.Aborted;
|
||
|
}
|
||
|
|
||
|
bool expired = !TimeoutHelper.WaitOne(this.waitHandle, timeout);
|
||
|
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (this.result == CommunicationWaitResult.Waiting)
|
||
|
{
|
||
|
this.result = (expired ? CommunicationWaitResult.Expired : CommunicationWaitResult.Succeeded);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lock (this.ThisLock)
|
||
|
{
|
||
|
if (!this.closed)
|
||
|
this.waitHandle.Set(); // unblock other waiters if there are any
|
||
|
}
|
||
|
|
||
|
return this.result;
|
||
|
}
|
||
|
}
|
||
|
}
|