You've already forked linux-packaging-mono
							
							
		
			
	
	
		
			1161 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			1161 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|   | //---------------------------------------------------------------------------- | ||
|  | // Copyright (c) Microsoft Corporation.  All rights reserved. | ||
|  | //---------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | namespace System.ServiceModel.Channels | ||
|  | { | ||
|  |     using System.Collections.Generic; | ||
|  |     using System.Diagnostics; | ||
|  |     using System.Runtime; | ||
|  |     using System.ServiceModel; | ||
|  |     using System.ServiceModel.Diagnostics; | ||
|  |     using System.Threading; | ||
|  |     using System.Xml; | ||
|  | 
 | ||
|  |     struct MessageAttemptInfo | ||
|  |     { | ||
|  |         readonly Message message; | ||
|  |         readonly int retryCount; | ||
|  |         readonly Int64 sequenceNumber; | ||
|  |         readonly object state; | ||
|  | 
 | ||
|  |         public MessageAttemptInfo(Message message, Int64 sequenceNumber, int retryCount, object state) | ||
|  |         { | ||
|  |             this.message = message; | ||
|  |             this.sequenceNumber = sequenceNumber; | ||
|  |             this.retryCount = retryCount; | ||
|  |             this.state = state; | ||
|  |         } | ||
|  |         public Message Message | ||
|  |         { | ||
|  |             get { return this.message; } | ||
|  |         } | ||
|  | 
 | ||
|  |         public int RetryCount | ||
|  |         { | ||
|  |             get { return this.retryCount; } | ||
|  |         } | ||
|  | 
 | ||
|  |         public object State | ||
|  |         { | ||
|  |             get { return this.state; } | ||
|  |         } | ||
|  | 
 | ||
|  |         public Int64 GetSequenceNumber() | ||
|  |         { | ||
|  |             if (this.sequenceNumber <= 0) | ||
|  |             { | ||
|  |                 throw Fx.AssertAndThrow("The caller is not allowed to get an invalid SequenceNumber."); | ||
|  |             } | ||
|  | 
 | ||
|  |             return this.sequenceNumber; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     sealed class TransmissionStrategy | ||
|  |     { | ||
|  |         bool aborted; | ||
|  |         bool closed; | ||
|  |         int congestionControlModeAcks; | ||
|  |         UniqueId id; | ||
|  |         Int64 last = 0; | ||
|  |         int lossWindowSize; | ||
|  |         int maxWindowSize; | ||
|  |         Int64 meanRtt; | ||
|  |         ComponentExceptionHandler onException; | ||
|  |         Int32 quotaRemaining; | ||
|  |         ReliableMessagingVersion reliableMessagingVersion; | ||
|  |         List<Int64> retransmissionWindow = new List<Int64>(); | ||
|  |         IOThreadTimer retryTimer; | ||
|  |         RetryHandler retryTimeoutElapsedHandler; | ||
|  |         bool requestAcks; | ||
|  |         Int64 serrRtt; | ||
|  |         int slowStartThreshold; | ||
|  |         bool startup = true; | ||
|  |         object thisLock = new object(); | ||
|  |         Int64 timeout; | ||
|  |         Queue<IQueueAdder> waitQueue = new Queue<IQueueAdder>(); | ||
|  |         SlidingWindow window; | ||
|  |         int windowSize = 1; | ||
|  |         Int64 windowStart = 1; | ||
|  | 
 | ||
|  |         public TransmissionStrategy(ReliableMessagingVersion reliableMessagingVersion, TimeSpan initRtt, | ||
|  |             int maxWindowSize, bool requestAcks, UniqueId id) | ||
|  |         { | ||
|  |             if (initRtt < TimeSpan.Zero) | ||
|  |             { | ||
|  |                 if (DiagnosticUtility.ShouldTrace(TraceEventType.Warning)) | ||
|  |                 { | ||
|  |                     TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.WsrmNegativeElapsedTimeDetected, | ||
|  |                     SR.GetString(SR.TraceCodeWsrmNegativeElapsedTimeDetected), this); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 initRtt = ReliableMessagingConstants.UnknownInitiationTime; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (maxWindowSize <= 0) | ||
|  |             { | ||
|  |                 throw Fx.AssertAndThrow("Argument maxWindow size must be positive."); | ||
|  |             } | ||
|  | 
 | ||
|  |             this.id = id; | ||
|  |             this.maxWindowSize = this.lossWindowSize = maxWindowSize; | ||
|  |             this.meanRtt = Math.Min((long)initRtt.TotalMilliseconds, Constants.MaxMeanRtt >> Constants.TimeMultiplier) << Constants.TimeMultiplier; | ||
|  |             this.serrRtt = this.meanRtt >> 1; | ||
|  |             this.window = new SlidingWindow(maxWindowSize); | ||
|  |             this.slowStartThreshold = maxWindowSize; | ||
|  |             this.timeout = Math.Max(((200 << Constants.TimeMultiplier) * 2) + this.meanRtt, this.meanRtt + (this.serrRtt << Constants.ChebychevFactor)); | ||
|  |             this.quotaRemaining = Int32.MaxValue; | ||
|  |             this.retryTimer = new IOThreadTimer(new Action<object>(OnRetryElapsed), null, true); | ||
|  |             this.requestAcks = requestAcks; | ||
|  |             this.reliableMessagingVersion = reliableMessagingVersion; | ||
|  |         } | ||
|  | 
 | ||
|  |         public bool DoneTransmitting | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return (this.last != 0 && this.windowStart == this.last + 1); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public bool HasPending | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return (this.window.Count > 0 || this.waitQueue.Count > 0); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public Int64 Last | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return this.last; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // now in 128ths of a millisecond. | ||
|  |         static Int64 Now | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return (Ticks.Now / TimeSpan.TicksPerMillisecond) << Constants.TimeMultiplier; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public ComponentExceptionHandler OnException | ||
|  |         { | ||
|  |             set | ||
|  |             { | ||
|  |                 this.onException = value; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public RetryHandler RetryTimeoutElapsed | ||
|  |         { | ||
|  |             set | ||
|  |             { | ||
|  |                 this.retryTimeoutElapsedHandler = value; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public int QuotaRemaining | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return this.quotaRemaining; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         object ThisLock | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return this.thisLock; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public int Timeout | ||
|  |         { | ||
|  |             get | ||
|  |             { | ||
|  |                 return (int)(this.timeout >> Constants.TimeMultiplier); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  | 
 | ||
|  |         public void Abort(ChannelBase channel) | ||
|  |         { | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 this.aborted = true; | ||
|  | 
 | ||
|  |                 if (this.closed) | ||
|  |                     return; | ||
|  | 
 | ||
|  |                 this.closed = true; | ||
|  | 
 | ||
|  |                 this.retryTimer.Cancel(); | ||
|  | 
 | ||
|  |                 while (waitQueue.Count > 0) | ||
|  |                     waitQueue.Dequeue().Abort(channel); | ||
|  | 
 | ||
|  |                 window.Close(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public bool Add(Message message, TimeSpan timeout, object state, out MessageAttemptInfo attemptInfo) | ||
|  |         { | ||
|  |             return InternalAdd(message, false, timeout, state, out attemptInfo); | ||
|  |         } | ||
|  | 
 | ||
|  |         public MessageAttemptInfo AddLast(Message message, TimeSpan timeout, object state) | ||
|  |         { | ||
|  |             if (this.reliableMessagingVersion != ReliableMessagingVersion.WSReliableMessagingFebruary2005) | ||
|  |             { | ||
|  |                 throw Fx.AssertAndThrow("Last message supported only in February 2005."); | ||
|  |             } | ||
|  | 
 | ||
|  |             MessageAttemptInfo attemptInfo = default(MessageAttemptInfo); | ||
|  |             InternalAdd(message, true, timeout, state, out attemptInfo); | ||
|  |             return attemptInfo; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Must call in a lock(this.ThisLock). | ||
|  |         MessageAttemptInfo AddToWindow(Message message, bool isLast, object state) | ||
|  |         { | ||
|  |             MessageAttemptInfo attemptInfo = default(MessageAttemptInfo); | ||
|  |             Int64 sequenceNumber; | ||
|  | 
 | ||
|  |             sequenceNumber = this.windowStart + this.window.Count; | ||
|  |             WsrmUtilities.AddSequenceHeader(this.reliableMessagingVersion, message, this.id, sequenceNumber, isLast); | ||
|  | 
 | ||
|  |             if (this.requestAcks && (this.window.Count == this.windowSize - 1 || this.quotaRemaining == 1)) // can't add any more | ||
|  |             { | ||
|  |                 message.Properties.AllowOutputBatching = false; | ||
|  |                 WsrmUtilities.AddAckRequestedHeader(this.reliableMessagingVersion, message, this.id); | ||
|  |             } | ||
|  | 
 | ||
|  |             if (this.window.Count == 0) | ||
|  |             { | ||
|  |                 this.retryTimer.Set(this.Timeout); | ||
|  |             } | ||
|  | 
 | ||
|  |             this.window.Add(message, Now, state); | ||
|  |             this.quotaRemaining--; | ||
|  |             if (isLast) | ||
|  |                 this.last = sequenceNumber; | ||
|  | 
 | ||
|  |             int index = (int)(sequenceNumber - this.windowStart); | ||
|  |             attemptInfo = new MessageAttemptInfo(this.window.GetMessage(index), sequenceNumber, 0, state); | ||
|  | 
 | ||
|  |             return attemptInfo; | ||
|  |         } | ||
|  | 
 | ||
|  |         public IAsyncResult BeginAdd(Message message, TimeSpan timeout, object state, AsyncCallback callback, object asyncState) | ||
|  |         { | ||
|  |             return InternalBeginAdd(message, false, timeout, state, callback, asyncState); | ||
|  |         } | ||
|  | 
 | ||
|  |         public IAsyncResult BeginAddLast(Message message, TimeSpan timeout, object state, AsyncCallback callback, object asyncState) | ||
|  |         { | ||
|  |             if (this.reliableMessagingVersion != ReliableMessagingVersion.WSReliableMessagingFebruary2005) | ||
|  |             { | ||
|  |                 throw Fx.AssertAndThrow("Last message supported only in February 2005."); | ||
|  |             } | ||
|  | 
 | ||
|  |             return InternalBeginAdd(message, true, timeout, state, callback, asyncState); | ||
|  |         } | ||
|  | 
 | ||
|  |         bool CanAdd() | ||
|  |         { | ||
|  |             return (this.window.Count < this.windowSize &&  // Does the message fit in the transmission window? | ||
|  |                 this.quotaRemaining > 0 &&                  // Can the receiver handle another message? | ||
|  |                 this.waitQueue.Count == 0);                 // Don't get ahead of anyone in the wait queue. | ||
|  |         } | ||
|  | 
 | ||
|  |         public void Close() | ||
|  |         { | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed) | ||
|  |                     return; | ||
|  | 
 | ||
|  |                 this.closed = true; | ||
|  | 
 | ||
|  |                 this.retryTimer.Cancel(); | ||
|  | 
 | ||
|  |                 if (waitQueue.Count != 0) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("The reliable channel must throw prior to the call to Close() if there are outstanding send or request operations."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 window.Close(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public void DequeuePending() | ||
|  |         { | ||
|  |             Queue<IQueueAdder> adders = null; | ||
|  | 
 | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed || this.waitQueue.Count == 0) | ||
|  |                     return; | ||
|  | 
 | ||
|  |                 int count = Math.Min(this.windowSize, this.quotaRemaining) - this.window.Count; | ||
|  |                 if (count <= 0) | ||
|  |                     return; | ||
|  | 
 | ||
|  |                 count = Math.Min(count, this.waitQueue.Count); | ||
|  |                 adders = new Queue<IQueueAdder>(count); | ||
|  | 
 | ||
|  |                 while (count-- > 0) | ||
|  |                 { | ||
|  |                     IQueueAdder adder = waitQueue.Dequeue(); | ||
|  |                     adder.Complete0(); | ||
|  |                     adders.Enqueue(adder); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             while (adders.Count > 0) | ||
|  |                 adders.Dequeue().Complete1(); | ||
|  |         } | ||
|  | 
 | ||
|  |         public bool EndAdd(IAsyncResult result, out MessageAttemptInfo attemptInfo) | ||
|  |         { | ||
|  |             return InternalEndAdd(result, out attemptInfo); | ||
|  |         } | ||
|  | 
 | ||
|  |         public MessageAttemptInfo EndAddLast(IAsyncResult result) | ||
|  |         { | ||
|  |             MessageAttemptInfo attemptInfo = default(MessageAttemptInfo); | ||
|  |             InternalEndAdd(result, out attemptInfo); | ||
|  |             return attemptInfo; | ||
|  |         } | ||
|  | 
 | ||
|  |         bool IsAddValid() | ||
|  |         { | ||
|  |             return (!this.aborted && !this.closed); | ||
|  |         } | ||
|  | 
 | ||
|  |         public void OnRetryElapsed(object state) | ||
|  |         { | ||
|  |             try | ||
|  |             { | ||
|  |                 MessageAttemptInfo attemptInfo = default(MessageAttemptInfo); | ||
|  | 
 | ||
|  |                 lock (this.ThisLock) | ||
|  |                 { | ||
|  |                     if (this.closed) | ||
|  |                         return; | ||
|  | 
 | ||
|  |                     if (this.window.Count == 0) | ||
|  |                         return; | ||
|  | 
 | ||
|  |                     this.window.RecordRetry(0, Now); | ||
|  |                     this.congestionControlModeAcks = 0; | ||
|  |                     this.slowStartThreshold = Math.Max(1, this.windowSize >> 1); | ||
|  |                     this.lossWindowSize = this.windowSize; | ||
|  |                     this.windowSize = 1; | ||
|  |                     this.timeout <<= 1; | ||
|  |                     this.startup = false; | ||
|  | 
 | ||
|  |                     attemptInfo = new MessageAttemptInfo(this.window.GetMessage(0), this.windowStart, this.window.GetRetryCount(0), this.window.GetState(0)); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 retryTimeoutElapsedHandler(attemptInfo); | ||
|  | 
 | ||
|  |                 lock (this.ThisLock) | ||
|  |                 { | ||
|  |                     if (!this.closed && (this.window.Count > 0)) | ||
|  |                     { | ||
|  |                         this.retryTimer.Set(this.Timeout); | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  | #pragma warning suppress 56500 // covered by FxCOP | ||
|  |             catch (Exception e) | ||
|  |             { | ||
|  |                 if (Fx.IsFatal(e)) | ||
|  |                     throw; | ||
|  | 
 | ||
|  |                 this.onException(e); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public void Fault(ChannelBase channel) | ||
|  |         { | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed) | ||
|  |                     return; | ||
|  | 
 | ||
|  |                 this.closed = true; | ||
|  | 
 | ||
|  |                 this.retryTimer.Cancel(); | ||
|  | 
 | ||
|  |                 while (waitQueue.Count > 0) | ||
|  |                     waitQueue.Dequeue().Fault(channel); | ||
|  | 
 | ||
|  |                 window.Close(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public MessageAttemptInfo GetMessageInfoForRetry(bool remove) | ||
|  |         { | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 // Closed, no need to retry. | ||
|  |                 if (this.closed) | ||
|  |                 { | ||
|  |                     return default(MessageAttemptInfo); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (remove) | ||
|  |                 { | ||
|  |                     if (this.retransmissionWindow.Count == 0) | ||
|  |                     { | ||
|  |                         throw Fx.AssertAndThrow("The caller is not allowed to remove a message attempt when there are no message attempts."); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     this.retransmissionWindow.RemoveAt(0); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 while (this.retransmissionWindow.Count > 0) | ||
|  |                 { | ||
|  |                     Int64 next = this.retransmissionWindow[0]; | ||
|  |                     if (next < this.windowStart) | ||
|  |                     { | ||
|  |                         // Already removed from the window, no need to retry. | ||
|  |                         this.retransmissionWindow.RemoveAt(0); | ||
|  |                     } | ||
|  |                     else | ||
|  |                     { | ||
|  |                         int index = (int)(next - this.windowStart); | ||
|  |                         if (this.window.GetTransferred(index)) | ||
|  |                             this.retransmissionWindow.RemoveAt(0); | ||
|  |                         else | ||
|  |                             return new MessageAttemptInfo(this.window.GetMessage(index), next, this.window.GetRetryCount(index), this.window.GetState(index)); | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 // Nothing left to retry. | ||
|  |                 return default(MessageAttemptInfo); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public bool SetLast() | ||
|  |         { | ||
|  |             if (this.reliableMessagingVersion != ReliableMessagingVersion.WSReliableMessaging11) | ||
|  |             { | ||
|  |                 throw Fx.AssertAndThrow("SetLast supported only in 1.1."); | ||
|  |             } | ||
|  | 
 | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.last != 0) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Cannot set last more than once."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 this.last = this.windowStart + this.window.Count - 1; | ||
|  |                 return (this.last == 0) || this.DoneTransmitting; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         bool InternalAdd(Message message, bool isLast, TimeSpan timeout, object state, out MessageAttemptInfo attemptInfo) | ||
|  |         { | ||
|  |             attemptInfo = default(MessageAttemptInfo); | ||
|  | 
 | ||
|  |             WaitQueueAdder adder; | ||
|  | 
 | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (isLast && this.last != 0) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Can't add more than one last message."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (!this.IsAddValid()) | ||
|  |                     return false; | ||
|  | 
 | ||
|  |                 ThrowIfRollover(); | ||
|  | 
 | ||
|  |                 if (CanAdd()) | ||
|  |                 { | ||
|  |                     attemptInfo = AddToWindow(message, isLast, state); | ||
|  |                     return true; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 adder = new WaitQueueAdder(this, message, isLast, state); | ||
|  |                 this.waitQueue.Enqueue(adder); | ||
|  |             } | ||
|  | 
 | ||
|  |             attemptInfo = adder.Wait(timeout); | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  |         IAsyncResult InternalBeginAdd(Message message, bool isLast, TimeSpan timeout, object state, AsyncCallback callback, object asyncState) | ||
|  |         { | ||
|  |             MessageAttemptInfo attemptInfo = default(MessageAttemptInfo); | ||
|  |             bool isAddValid; | ||
|  | 
 | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (isLast && this.last != 0) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Can't add more than one last message."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 isAddValid = this.IsAddValid(); | ||
|  | 
 | ||
|  |                 if (isAddValid) | ||
|  |                 { | ||
|  |                     ThrowIfRollover(); | ||
|  | 
 | ||
|  |                     if (CanAdd()) | ||
|  |                     { | ||
|  |                         attemptInfo = AddToWindow(message, isLast, state); | ||
|  |                     } | ||
|  |                     else | ||
|  |                     { | ||
|  |                         AsyncQueueAdder adder = new AsyncQueueAdder(message, isLast, timeout, state, this, callback, asyncState); | ||
|  |                         this.waitQueue.Enqueue(adder); | ||
|  | 
 | ||
|  |                         return adder; | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             return new CompletedAsyncResult<bool, MessageAttemptInfo>(isAddValid, attemptInfo, callback, asyncState); | ||
|  |         } | ||
|  | 
 | ||
|  |         bool InternalEndAdd(IAsyncResult result, out MessageAttemptInfo attemptInfo) | ||
|  |         { | ||
|  |             if (result is CompletedAsyncResult<bool, MessageAttemptInfo>) | ||
|  |             { | ||
|  |                 return CompletedAsyncResult<bool, MessageAttemptInfo>.End(result, out attemptInfo); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 attemptInfo = AsyncQueueAdder.End((AsyncQueueAdder)result); | ||
|  |                 return true; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public bool IsFinalAckConsistent(SequenceRangeCollection ranges) | ||
|  |         { | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed) | ||
|  |                 { | ||
|  |                     return true; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 // Nothing sent, ensure ack is empty. | ||
|  |                 if ((this.windowStart == 1) && (this.window.Count == 0)) | ||
|  |                 { | ||
|  |                     return ranges.Count == 0; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 // Ack is empty or first range is invalid. | ||
|  |                 if (ranges.Count == 0 || ranges[0].Lower != 1) | ||
|  |                 { | ||
|  |                     return false; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return ranges[0].Upper >= (this.windowStart - 1); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public void ProcessAcknowledgement(SequenceRangeCollection ranges, out bool invalidAck, out bool inconsistentAck) | ||
|  |         { | ||
|  |             invalidAck = false; | ||
|  |             inconsistentAck = false; | ||
|  |             bool newAck = false; | ||
|  |             bool oldAck = false; | ||
|  | 
 | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed) | ||
|  |                 { | ||
|  |                     return; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 Int64 lastMessageSent = this.windowStart + this.window.Count - 1; | ||
|  |                 Int64 lastMessageAcked = this.windowStart - 1; | ||
|  |                 int transferredInWindow = this.window.TransferredCount; | ||
|  | 
 | ||
|  |                 for (int i = 0; i < ranges.Count; i++) | ||
|  |                 { | ||
|  |                     SequenceRange range = ranges[i]; | ||
|  | 
 | ||
|  |                     // Ack for a message not yet sent. | ||
|  |                     if (range.Upper > lastMessageSent) | ||
|  |                     { | ||
|  |                         invalidAck = true; | ||
|  |                         return; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     if (((range.Lower > 1) && (range.Lower <= lastMessageAcked)) || (range.Upper < lastMessageAcked)) | ||
|  |                     { | ||
|  |                         oldAck = true; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     if (range.Upper >= this.windowStart) | ||
|  |                     { | ||
|  |                         if (range.Lower <= this.windowStart) | ||
|  |                         { | ||
|  |                             newAck = true; | ||
|  |                         } | ||
|  | 
 | ||
|  |                         if (!newAck) | ||
|  |                         { | ||
|  |                             int beginIndex = (int)(range.Lower - this.windowStart); | ||
|  |                             int endIndex = (int)((range.Upper > lastMessageSent) ? (this.window.Count - 1) : (range.Upper - this.windowStart)); | ||
|  | 
 | ||
|  |                             newAck = this.window.GetTransferredInRangeCount(beginIndex, endIndex) < (endIndex - beginIndex + 1); | ||
|  |                         } | ||
|  | 
 | ||
|  |                         if (transferredInWindow > 0 && !oldAck) | ||
|  |                         { | ||
|  |                             int beginIndex = (int)((range.Lower < this.windowStart) ? 0 : (range.Lower - this.windowStart)); | ||
|  |                             int endIndex = (int)((range.Upper > lastMessageSent) ? (this.window.Count - 1) : (range.Upper - this.windowStart)); | ||
|  | 
 | ||
|  |                             transferredInWindow -= this.window.GetTransferredInRangeCount(beginIndex, endIndex); | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (transferredInWindow > 0) | ||
|  |                     oldAck = true; | ||
|  |             } | ||
|  | 
 | ||
|  |             inconsistentAck = oldAck && newAck; | ||
|  |         } | ||
|  | 
 | ||
|  |         // Called for RequestReply. | ||
|  |         // Argument transferred is the request sequence number and it is assumed to be positive. | ||
|  |         public bool ProcessTransferred(Int64 transferred, int quotaRemaining) | ||
|  |         { | ||
|  |             if (transferred <= 0) | ||
|  |             { | ||
|  |                 throw Fx.AssertAndThrow("Argument transferred must be a valid sequence number."); | ||
|  |             } | ||
|  | 
 | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed) | ||
|  |                 { | ||
|  |                     return false; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return ProcessTransferred(new SequenceRange(transferred), quotaRemaining); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // Called for Duplex and Output | ||
|  |         public bool ProcessTransferred(SequenceRangeCollection ranges, int quotaRemaining) | ||
|  |         { | ||
|  |             if (ranges.Count == 0) | ||
|  |             { | ||
|  |                 return false; | ||
|  |             } | ||
|  | 
 | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed) | ||
|  |                 { | ||
|  |                     return false; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 bool send = false; | ||
|  | 
 | ||
|  |                 for (int rangeIndex = 0; rangeIndex < ranges.Count; rangeIndex++) | ||
|  |                 { | ||
|  |                     if (this.ProcessTransferred(ranges[rangeIndex], quotaRemaining)) | ||
|  |                     { | ||
|  |                         send = true; | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return send; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // It is necessary that ProcessAcknowledgement be called prior, as  | ||
|  |         // this method does not check for valid ack ranges. | ||
|  |         // This method returns true if the calling method should start sending retries  | ||
|  |         // obtained from GetMessageInfoForRetry. | ||
|  |         bool ProcessTransferred(SequenceRange range, int quotaRemaining) | ||
|  |         { | ||
|  |             if (range.Upper < this.windowStart) | ||
|  |             { | ||
|  |                 if (range.Upper == this.windowStart - 1 && (quotaRemaining != -1) && quotaRemaining > this.quotaRemaining) | ||
|  |                     this.quotaRemaining = quotaRemaining - Math.Min(this.windowSize, this.window.Count); | ||
|  | 
 | ||
|  |                 return false; | ||
|  |             } | ||
|  |             else if (range.Lower <= this.windowStart) | ||
|  |             { | ||
|  |                 bool send = false; | ||
|  | 
 | ||
|  |                 this.retryTimer.Cancel(); | ||
|  | 
 | ||
|  |                 Int64 slide = range.Upper - this.windowStart + 1; | ||
|  | 
 | ||
|  |                 // For Request Reply: Requests are transferred 1 at a time, (i.e. when the reply comes back). | ||
|  |                 // The TransmissionStrategy only removes messages if the window start is removed. | ||
|  |                 // Because of this, RequestReply messages transferred out of order will cause many, many retries. | ||
|  |                 // To avoid extraneous retries we mark each message transferred, and we remove our virtual slide. | ||
|  |                 if (slide == 1) | ||
|  |                 { | ||
|  |                     for (int i = 1; i < this.window.Count; i++) | ||
|  |                     { | ||
|  |                         if (this.window.GetTransferred(i)) | ||
|  |                         { | ||
|  |                             slide++; | ||
|  |                         } | ||
|  |                         else | ||
|  |                         { | ||
|  |                             break; | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 Int64 now = Now; | ||
|  |                 Int64 oldWindowEnd = this.windowStart + this.windowSize; | ||
|  | 
 | ||
|  |                 for (int i = 0; i < (int)slide; i++) | ||
|  |                     UpdateStats(now, this.window.GetLastAttemptTime(i)); | ||
|  | 
 | ||
|  |                 if (quotaRemaining != -1) | ||
|  |                 { | ||
|  |                     int inFlightAfterAck = Math.Min(this.windowSize, this.window.Count) - (int)slide; | ||
|  |                     this.quotaRemaining = quotaRemaining - Math.Max(0, inFlightAfterAck); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 this.window.Remove((int)slide); | ||
|  | 
 | ||
|  |                 this.windowStart += slide; | ||
|  | 
 | ||
|  |                 int sendBeginIndex = 0; | ||
|  | 
 | ||
|  |                 if (this.windowSize <= this.slowStartThreshold) | ||
|  |                 { | ||
|  |                     this.windowSize = Math.Min(this.maxWindowSize, Math.Min(this.slowStartThreshold + 1, this.windowSize + (int)slide)); | ||
|  | 
 | ||
|  |                     if (!startup) | ||
|  |                         sendBeginIndex = 0; | ||
|  |                     else | ||
|  |                         sendBeginIndex = Math.Max(0, (int)oldWindowEnd - (int)this.windowStart); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     this.congestionControlModeAcks += (int)slide; | ||
|  | 
 | ||
|  |                     // EXPERIMENTAL, needs optimizing /// | ||
|  |                     int segmentSize = Math.Max(1, (this.lossWindowSize - this.slowStartThreshold) / 8); | ||
|  |                     int windowGrowthAckThreshold = ((this.windowSize - this.slowStartThreshold) * this.windowSize) / segmentSize; | ||
|  | 
 | ||
|  |                     if (this.congestionControlModeAcks > windowGrowthAckThreshold) | ||
|  |                     { | ||
|  |                         this.congestionControlModeAcks = 0; | ||
|  |                         this.windowSize = Math.Min(this.maxWindowSize, this.windowSize + 1); | ||
|  |                     } | ||
|  | 
 | ||
|  |                     sendBeginIndex = Math.Max(0, (int)oldWindowEnd - (int)this.windowStart); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 int sendEndIndex = Math.Min(this.windowSize, this.window.Count); | ||
|  | 
 | ||
|  |                 if (sendBeginIndex < sendEndIndex) | ||
|  |                 { | ||
|  |                     send = (this.retransmissionWindow.Count == 0); | ||
|  | 
 | ||
|  |                     for (int i = sendBeginIndex; i < this.windowSize && i < this.window.Count; i++) | ||
|  |                     { | ||
|  |                         Int64 sequenceNumber = this.windowStart + i; | ||
|  | 
 | ||
|  |                         if (!this.window.GetTransferred(i) && !this.retransmissionWindow.Contains(sequenceNumber)) | ||
|  |                         { | ||
|  |                             this.window.RecordRetry(i, Now); | ||
|  |                             retransmissionWindow.Add(sequenceNumber); | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (window.Count > 0) | ||
|  |                 { | ||
|  |                     this.retryTimer.Set(this.Timeout); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return send; | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 for (Int64 i = range.Lower; i <= range.Upper; i++) | ||
|  |                 { | ||
|  |                     this.window.SetTransferred((int)(i - this.windowStart)); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         bool RemoveAdder(IQueueAdder adder) | ||
|  |         { | ||
|  |             lock (this.ThisLock) | ||
|  |             { | ||
|  |                 if (this.closed) | ||
|  |                     return false; | ||
|  | 
 | ||
|  |                 bool removed = false; | ||
|  |                 for (int i = 0; i < this.waitQueue.Count; i++) | ||
|  |                 { | ||
|  |                     IQueueAdder current = this.waitQueue.Dequeue(); | ||
|  | 
 | ||
|  |                     if (Object.ReferenceEquals(adder, current)) | ||
|  |                         removed = true; | ||
|  |                     else | ||
|  |                         this.waitQueue.Enqueue(current); | ||
|  |                 } | ||
|  |                 return removed; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         void ThrowIfRollover() | ||
|  |         { | ||
|  |             if (this.windowStart + this.window.Count + this.waitQueue.Count == Int64.MaxValue) | ||
|  |                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageNumberRolloverFault(this.id).CreateException()); | ||
|  |         } | ||
|  | 
 | ||
|  |         void UpdateStats(Int64 now, Int64 lastAttemptTime) | ||
|  |         { | ||
|  |             now = Math.Max(now, lastAttemptTime); | ||
|  |             Int64 measuredRtt = now - lastAttemptTime; | ||
|  |             Int64 error = measuredRtt - this.meanRtt; | ||
|  |             this.serrRtt = Math.Min(this.serrRtt + ((Math.Abs(error) - this.serrRtt) >> Constants.Gain), Constants.MaxSerrRtt); | ||
|  |             this.meanRtt = Math.Min(this.meanRtt + (error >> Constants.Gain), Constants.MaxMeanRtt); | ||
|  |             this.timeout = Math.Max(((200 << Constants.TimeMultiplier) * 2) + this.meanRtt, this.meanRtt + (this.serrRtt << Constants.ChebychevFactor)); | ||
|  |         } | ||
|  | 
 | ||
|  |         class AsyncQueueAdder : WaitAsyncResult, IQueueAdder | ||
|  |         { | ||
|  |             bool isLast; | ||
|  |             MessageAttemptInfo attemptInfo = default(MessageAttemptInfo); | ||
|  |             TransmissionStrategy strategy; | ||
|  | 
 | ||
|  |             public AsyncQueueAdder(Message message, bool isLast, TimeSpan timeout, object state, TransmissionStrategy strategy, AsyncCallback callback, object asyncState) | ||
|  |                 : base(timeout, true, callback, asyncState) | ||
|  |             { | ||
|  |                 // MessageAttemptInfo(Message message, Int64 sequenceNumber, int retryCount, object state) | ||
|  |                 // this.attemptInfo is just a state bag, thus sequenceNumber can be 0 and should never be read. | ||
|  |                 this.attemptInfo = new MessageAttemptInfo(message, 0, 0, state); | ||
|  |                 this.isLast = isLast; | ||
|  |                 this.strategy = strategy; | ||
|  |                 base.Begin(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Abort(CommunicationObject communicationObject) | ||
|  |             { | ||
|  |                 this.attemptInfo.Message.Close(); | ||
|  |                 OnAborted(communicationObject); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Complete0() | ||
|  |             { | ||
|  |                 this.attemptInfo = strategy.AddToWindow(this.attemptInfo.Message, this.isLast, this.attemptInfo.State); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Complete1() | ||
|  |             { | ||
|  |                 OnSignaled(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public static MessageAttemptInfo End(AsyncQueueAdder result) | ||
|  |             { | ||
|  |                 AsyncResult.End<AsyncQueueAdder>(result); | ||
|  |                 return result.attemptInfo; | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Fault(CommunicationObject communicationObject) | ||
|  |             { | ||
|  |                 this.attemptInfo.Message.Close(); | ||
|  |                 OnFaulted(communicationObject); | ||
|  |             } | ||
|  | 
 | ||
|  |             protected override string GetTimeoutString(TimeSpan timeout) | ||
|  |             { | ||
|  |                 return SR.GetString(SR.TimeoutOnAddToWindow, timeout); | ||
|  |             } | ||
|  | 
 | ||
|  |             protected override void OnTimerElapsed(object state) | ||
|  |             { | ||
|  |                 if (this.strategy.RemoveAdder(this)) | ||
|  |                     base.OnTimerElapsed(state); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         static class Constants | ||
|  |         { | ||
|  |             // Used to adjust the timeout calculation, according to Chebychev's theorem, | ||
|  |             // to fit ~98% of actual rtt's within our timeout. | ||
|  |             public const int ChebychevFactor = 2; | ||
|  | 
 | ||
|  |             // Gain of 0.125 (1/8). Shift right by 3 to apply the gain to a term. | ||
|  |             public const int Gain = 3; | ||
|  | 
 | ||
|  |             // 1ms == 128 of our time units. Shift left by 7 to perform the multiplication. | ||
|  |             public const int TimeMultiplier = 7; | ||
|  | 
 | ||
|  |             // These guarantee no overflows when calculating timeout. | ||
|  |             public const long MaxMeanRtt = long.MaxValue / 3; | ||
|  |             public const long MaxSerrRtt = MaxMeanRtt / 2; | ||
|  |         } | ||
|  | 
 | ||
|  |         interface IQueueAdder | ||
|  |         { | ||
|  |             void Abort(CommunicationObject communicationObject); | ||
|  |             void Fault(CommunicationObject communicationObject); | ||
|  |             void Complete0(); | ||
|  |             void Complete1(); | ||
|  |         } | ||
|  | 
 | ||
|  |         class SlidingWindow | ||
|  |         { | ||
|  |             TransmissionInfo[] buffer; | ||
|  |             int head = 0; | ||
|  |             int tail = 0; | ||
|  |             int maxSize; | ||
|  | 
 | ||
|  |             public SlidingWindow(int maxSize) | ||
|  |             { | ||
|  |                 this.maxSize = maxSize + 1; | ||
|  |                 this.buffer = new TransmissionInfo[this.maxSize]; | ||
|  |             } | ||
|  | 
 | ||
|  |             public int Count | ||
|  |             { | ||
|  |                 get | ||
|  |                 { | ||
|  |                     if (this.tail >= this.head) | ||
|  |                         return (this.tail - this.head); | ||
|  |                     else | ||
|  |                         return (this.tail - this.head + this.maxSize); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public int TransferredCount | ||
|  |             { | ||
|  |                 get | ||
|  |                 { | ||
|  |                     if (this.Count == 0) | ||
|  |                         return 0; | ||
|  |                     else | ||
|  |                         return this.GetTransferredInRangeCount(0, this.Count - 1); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Add(Message message, Int64 addTime, object state) | ||
|  |             { | ||
|  |                 if (this.Count >= (this.maxSize - 1)) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("The caller is not allowed to add messages beyond the sliding window's maximum size."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 this.buffer[this.tail] = new TransmissionInfo(message, addTime, state); | ||
|  |                 this.tail = (this.tail + 1) % this.maxSize; | ||
|  |             } | ||
|  | 
 | ||
|  |             void AssertIndex(int index) | ||
|  |             { | ||
|  |                 if (index >= Count) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Argument index must be less than Count."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (index < 0) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Argument index must be positive."); | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Close() | ||
|  |             { | ||
|  |                 this.Remove(Count); | ||
|  |             } | ||
|  | 
 | ||
|  |             public Int64 GetLastAttemptTime(int index) | ||
|  |             { | ||
|  |                 this.AssertIndex(index); | ||
|  |                 return this.buffer[(head + index) % this.maxSize].LastAttemptTime; | ||
|  |             } | ||
|  | 
 | ||
|  |             public Message GetMessage(int index) | ||
|  |             { | ||
|  |                 this.AssertIndex(index); | ||
|  |                 if (!this.buffer[(head + index) % this.maxSize].Transferred) | ||
|  |                     return this.buffer[(head + index) % this.maxSize].Buffer.CreateMessage(); | ||
|  |                 else | ||
|  |                     return null; | ||
|  |             } | ||
|  | 
 | ||
|  |             public int GetRetryCount(int index) | ||
|  |             { | ||
|  |                 this.AssertIndex(index); | ||
|  |                 return this.buffer[(this.head + index) % this.maxSize].RetryCount; | ||
|  |             } | ||
|  | 
 | ||
|  |             public object GetState(int index) | ||
|  |             { | ||
|  |                 this.AssertIndex(index); | ||
|  |                 return this.buffer[(this.head + index) % this.maxSize].State; | ||
|  |             } | ||
|  | 
 | ||
|  |             public bool GetTransferred(int index) | ||
|  |             { | ||
|  |                 this.AssertIndex(index); | ||
|  |                 return this.buffer[(this.head + index) % this.maxSize].Transferred; | ||
|  |             } | ||
|  | 
 | ||
|  |             public int GetTransferredInRangeCount(int beginIndex, int endIndex) | ||
|  |             { | ||
|  |                 if (beginIndex < 0) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Argument beginIndex cannot be negative."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (endIndex >= this.Count) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Argument endIndex cannot be greater than Count."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (endIndex < beginIndex) | ||
|  |                 { | ||
|  |                     throw Fx.AssertAndThrow("Argument endIndex cannot be less than argument beginIndex."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 int result = 0; | ||
|  | 
 | ||
|  |                 for (int index = beginIndex; index <= endIndex; index++) | ||
|  |                 { | ||
|  |                     if (this.buffer[(head + index) % this.maxSize].Transferred) | ||
|  |                         result++; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 return result; | ||
|  |             } | ||
|  | 
 | ||
|  |             public int RecordRetry(int index, Int64 retryTime) | ||
|  |             { | ||
|  |                 this.AssertIndex(index); | ||
|  |                 this.buffer[(head + index) % this.maxSize].LastAttemptTime = retryTime; | ||
|  | 
 | ||
|  |                 return ++this.buffer[(head + index) % this.maxSize].RetryCount; | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Remove(int count) | ||
|  |             { | ||
|  |                 if (count > this.Count) | ||
|  |                 { | ||
|  |                     Fx.Assert("Cannot remove more messages than the window's Count."); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 while (count-- > 0) | ||
|  |                 { | ||
|  |                     this.buffer[head].Buffer.Close(); | ||
|  |                     this.buffer[head].Buffer = null; | ||
|  |                     this.head = (this.head + 1) % this.maxSize; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             public void SetTransferred(int index) | ||
|  |             { | ||
|  |                 this.AssertIndex(index); | ||
|  |                 this.buffer[(head + index) % this.maxSize].Transferred = true; | ||
|  |             } | ||
|  | 
 | ||
|  |             struct TransmissionInfo | ||
|  |             { | ||
|  |                 internal MessageBuffer Buffer; | ||
|  |                 internal Int64 LastAttemptTime; | ||
|  |                 internal int RetryCount; | ||
|  |                 internal object State; | ||
|  |                 internal bool Transferred; | ||
|  | 
 | ||
|  |                 public TransmissionInfo(Message message, Int64 lastAttemptTime, object state) | ||
|  |                 { | ||
|  |                     this.Buffer = message.CreateBufferedCopy(int.MaxValue); | ||
|  |                     this.LastAttemptTime = lastAttemptTime; | ||
|  |                     this.RetryCount = 0; | ||
|  |                     this.State = state; | ||
|  |                     this.Transferred = false; | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         class WaitQueueAdder : IQueueAdder | ||
|  |         { | ||
|  |             ManualResetEvent completeEvent = new ManualResetEvent(false); | ||
|  |             Exception exception; | ||
|  |             bool isLast; | ||
|  |             MessageAttemptInfo attemptInfo = default(MessageAttemptInfo); | ||
|  |             TransmissionStrategy strategy; | ||
|  | 
 | ||
|  |             public WaitQueueAdder(TransmissionStrategy strategy, Message message, bool isLast, object state) | ||
|  |             { | ||
|  |                 this.strategy = strategy; | ||
|  |                 this.isLast = isLast; | ||
|  |                 this.attemptInfo = new MessageAttemptInfo(message, 0, 0, state); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Abort(CommunicationObject communicationObject) | ||
|  |             { | ||
|  |                 this.exception = communicationObject.CreateClosedException(); | ||
|  |                 completeEvent.Set(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Complete0() | ||
|  |             { | ||
|  |                 attemptInfo = this.strategy.AddToWindow(this.attemptInfo.Message, this.isLast, this.attemptInfo.State); | ||
|  |                 this.completeEvent.Set(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Complete1() | ||
|  |             { | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Fault(CommunicationObject communicationObject) | ||
|  |             { | ||
|  |                 this.exception = communicationObject.GetTerminalException(); | ||
|  |                 completeEvent.Set(); | ||
|  |             } | ||
|  | 
 | ||
|  |             public MessageAttemptInfo Wait(TimeSpan timeout) | ||
|  |             { | ||
|  |                 if (!TimeoutHelper.WaitOne(this.completeEvent, timeout)) | ||
|  |                 { | ||
|  |                     if (this.strategy.RemoveAdder(this) && this.exception == null) | ||
|  |                         this.exception = new TimeoutException(SR.GetString(SR.TimeoutOnAddToWindow, timeout)); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (this.exception != null) | ||
|  |                 { | ||
|  |                     this.attemptInfo.Message.Close(); | ||
|  |                     this.completeEvent.Close(); | ||
|  |                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(this.exception); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 // This is safe because, Abort, Complete0, Fault, and RemoveAdder all occur under  | ||
|  |                 // the TransmissionStrategy's lock and RemoveAdder ensures that the  | ||
|  |                 // TransmissionStrategy will never call into this object again. | ||
|  |                 this.completeEvent.Close(); | ||
|  |                 return this.attemptInfo; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |