| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.ServiceModel.Channels
- {
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Runtime;
- using System.Threading;
- partial class PeerNodeImplementation
- {
- // A simple state manager for the PeerNode. Unlike the state managers used for channels and other
- // classes, a PeerNode's Open/Close is counted, a PeerNode is re-openable, and Abort only
- // takes effect if the outstanding number of Opens is 1.
- // The PeerNode defers to this object for all state related operations.
- //
- // Whenever a call is made that may change the state of the object (openCount transitions between 0 and 1),
- // an operation is queued. When an operation is removed from the queue, if the target state is still the
- // same as the operation (e.g. openCount > 0 and operation == Open) and the object is not already in that
- // state, the operation is performed by calling back into the PeerNode
- //
- // Because each operation is pulled form the queue one at a time, the open and close of the
- // PeerNode is serialized
- class SimpleStateManager
- {
- internal enum State { NotOpened, Opening, Opened, Closing };
- State currentState = State.NotOpened;
- object thisLock = new object();
- Queue<IOperation> queue = new Queue<IOperation>();
- bool queueRunning;
- int openCount;
- PeerNodeImplementation peerNode;
- public SimpleStateManager(PeerNodeImplementation peerNode)
- {
- this.peerNode = peerNode;
- }
- object ThisLock
- {
- get { return thisLock; }
- }
- public void Abort()
- {
- lock (ThisLock)
- {
- bool runAbort = false;
- if (openCount <= 1 && currentState != State.NotOpened)
- {
- runAbort = true;
- }
- if (openCount > 0)
- {
- --openCount;
- }
- if (runAbort)
- {
- try
- {
- peerNode.OnAbort();
- }
- finally
- {
- currentState = State.NotOpened;
- }
- }
- }
- }
- public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
- {
- CloseOperation op = null;
- lock (ThisLock)
- {
- if (openCount > 0)
- {
- --openCount;
- }
- if (openCount > 0)
- {
- return new CompletedAsyncResult(callback, state);
- }
- else
- {
- op = new CloseOperation(this, peerNode, timeout, callback, state);
- queue.Enqueue(op);
- RunQueue();
- }
- }
- return op;
- }
- public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state, bool waitForOnline)
- {
- bool completedSynchronously = false;
- OpenOperation op = null;
- lock (ThisLock)
- {
- openCount++;
- if (openCount > 1 && currentState == State.Opened)
- {
- completedSynchronously = true;
- }
- else
- {
- op = new OpenOperation(this, peerNode, timeout, callback, state, waitForOnline);
- queue.Enqueue(op);
- RunQueue();
- }
- }
- if (completedSynchronously)
- {
- return new CompletedAsyncResult(callback, state);
- }
- return op;
- }
- public void Close(TimeSpan timeout)
- {
- EndClose(BeginClose(timeout, null, null));
- }
- public static void EndOpen(IAsyncResult result)
- {
- // result can be either an OpenOperation or a CompletedAsyncResult
- if (result is CompletedAsyncResult)
- CompletedAsyncResult.End(result);
- else
- OpenOperation.End(result);
- }
- public static void EndClose(IAsyncResult result)
- {
- // result can be either an CloseOperation or a CompletedAsyncResult
- if (result is CompletedAsyncResult)
- CompletedAsyncResult.End(result);
- else
- CloseOperation.End(result);
- }
- // Process IP Address change event from IP helper
- public void OnIPAddressesChanged(object sender, EventArgs e)
- {
- IPAddressChangeOperation op = null;
- lock (ThisLock)
- {
- op = new IPAddressChangeOperation(peerNode);
- queue.Enqueue(op);
- RunQueue();
- }
- }
- public void Open(TimeSpan timeout, bool waitForOnline)
- {
- EndOpen(BeginOpen(timeout, null, null, waitForOnline));
- }
- // Start running operations from the queue (must be called within lock)
- void RunQueue()
- {
- if (queueRunning)
- return;
- queueRunning = true;
- ActionItem.Schedule(new Action<object>(RunQueueCallback), null);
- }
- void RunQueueCallback(object state)
- {
- IOperation op;
- // remove an operation from the queue
- lock (ThisLock)
- {
- Fx.Assert(queue.Count > 0, "queue should not be empty");
- op = queue.Dequeue();
- }
- try
- {
- // execute the operation
- op.Run();
- }
- finally
- {
- lock (ThisLock)
- {
- // if there are still pending operations, schedule another thread
- if (queue.Count > 0)
- {
- try
- {
- ActionItem.Schedule(new Action<object>(RunQueueCallback), null);
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e)) throw;
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- }
- else
- {
- queueRunning = false;
- }
- }
- }
- }
- interface IOperation
- {
- void Run();
- }
- class CloseOperation : OperationBase
- {
- PeerNodeImplementation peerNode;
- public CloseOperation(SimpleStateManager stateManager,
- PeerNodeImplementation peerNode, TimeSpan timeout, AsyncCallback callback, object state)
- : base(stateManager, timeout, callback, state)
- {
- this.peerNode = peerNode;
- }
- protected override void Run()
- {
- Exception lclException = null;
- try
- {
- lock (ThisLock)
- {
- if (stateManager.openCount > 0)
- {
- // the current target state is no longer Closed
- invokeOperation = false;
- }
- else if (stateManager.currentState == State.NotOpened)
- {
- // the state is already Closed
- invokeOperation = false;
- }
- else if (timeoutHelper.RemainingTime() <= TimeSpan.Zero)
- {
- // Time out has already happened complete will be taken care of in the
- // OperationBase class
- invokeOperation = false;
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException());
- }
- else
- {
- // the PeerNode needs to be closed
- if (!(stateManager.currentState != State.Opening && stateManager.currentState != State.Closing))
- {
- throw Fx.AssertAndThrow("Open and close are serialized by queue We should not be either in Closing or Opening state at this point");
- }
- if (stateManager.currentState != State.NotOpened)
- {
- stateManager.currentState = State.Closing;
- invokeOperation = true;
- }
- }
- }
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e)) throw;
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- lclException = e;
- }
- if (invokeOperation)
- {
- try
- {
- peerNode.OnClose(timeoutHelper.RemainingTime());
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e)) throw;
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- lclException = e;
- }
- lock (ThisLock)
- {
- stateManager.currentState = State.NotOpened;
- }
- }
- Complete(lclException);
- }
- }
- class OpenOperation : OperationBase
- {
- PeerNodeImplementation peerNode;
- bool waitForOnline;
- public OpenOperation(SimpleStateManager stateManager, PeerNodeImplementation peerNode, TimeSpan timeout,
- AsyncCallback callback, object state, bool waitForOnline)
- : base(stateManager, timeout, callback, state)
- {
- this.peerNode = peerNode;
- this.waitForOnline = waitForOnline;
- }
- protected override void Run()
- {
- Exception lclException = null;
- try
- {
- lock (ThisLock)
- {
- if (stateManager.openCount < 1)
- {
- // the current target state is no longer Opened
- invokeOperation = false;
- }
- else if (stateManager.currentState == State.Opened)
- {
- // the state is already Opened
- invokeOperation = false;
- }
- else if (timeoutHelper.RemainingTime() <= TimeSpan.Zero)
- {
- // Time out has already happened complete will be taken care of in the
- // OperationBase class
- invokeOperation = false;
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException());
- }
- else
- {
- // the PeerNode needs to be opened
- if (!(stateManager.currentState != State.Opening && stateManager.currentState != State.Closing))
- {
- throw Fx.AssertAndThrow("Open and close are serialized by queue We should not be either in Closing or Opening state at this point");
- }
- if (stateManager.currentState != State.Opened)
- {
- stateManager.currentState = State.Opening;
- invokeOperation = true;
- }
- }
- }
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e)) throw;
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- lclException = e;
- }
- if (invokeOperation)
- {
- try
- {
- peerNode.OnOpen(timeoutHelper.RemainingTime(), waitForOnline);
- lock (ThisLock)
- {
- stateManager.currentState = State.Opened;
- }
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e)) throw;
- lock (ThisLock)
- {
- stateManager.currentState = State.NotOpened;
- // since Open is throwing, we roll back the openCount because a matching Close is not
- // expected
- stateManager.openCount--;
- }
- lclException = e;
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- }
- Complete(lclException);
- }
- }
- // Base class for Open and Cose
- abstract class OperationBase : AsyncResult, IOperation
- {
- protected SimpleStateManager stateManager;
- protected TimeoutHelper timeoutHelper;
- AsyncCallback callback;
- protected bool invokeOperation;
-
- // Double-checked locking pattern requires volatile for read/write synchronization
- volatile bool completed;
- public OperationBase(SimpleStateManager stateManager, TimeSpan timeout,
- AsyncCallback callback, object state)
- : base(callback, state)
- {
- this.stateManager = stateManager;
- timeoutHelper = new TimeoutHelper(timeout);
- this.callback = callback;
- invokeOperation = false;
- completed = false;
- }
- void AsyncComplete(object o)
- {
- try
- {
- base.Complete(false, (Exception)o);
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e)) throw;
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(SR.GetString(SR.AsyncCallbackException), e);
- }
- }
- protected abstract void Run();
- void IOperation.Run()
- {
- Run();
- }
- protected void Complete(Exception exception)
- {
- if (completed)
- {
- return;
- }
- lock (ThisLock)
- {
- if (completed)
- {
- return;
- }
- completed = true;
- }
- try
- {
- if (callback != null)
- {
- // complete the AsyncResult on a separate thread so that the queue can progress.
- // this prevents a deadlock when the callback attempts to call Close.
- // this may cause the callbacks to be called in a differnet order in which they completed, but that
- // is ok because each callback is associated with a different object (channel or listener factory)
- ActionItem.Schedule(new Action<object>(AsyncComplete), exception);
- }
- else
- {
- AsyncComplete(exception);
- }
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e)) throw;
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(SR.GetString(SR.MessagePropagationException), e);
- }
- }
- protected object ThisLock
- {
- get { return stateManager.thisLock; }
- }
- static public void End(IAsyncResult result)
- {
- AsyncResult.End<OperationBase>(result);
- }
- }
- // To serialize IP address change processing
- class IPAddressChangeOperation : IOperation
- {
- PeerNodeImplementation peerNode;
- public IPAddressChangeOperation(PeerNodeImplementation peerNode)
- {
- this.peerNode = peerNode;
- }
- void IOperation.Run()
- {
- peerNode.OnIPAddressChange();
- }
- }
- }
- }
- }
|