1 //----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------------------
4 namespace System.ServiceModel.Channels
5 {
6     using System.Runtime;
7     using System.Threading;
8     using System.Xml;
9 
BeginSendHandler(MessageAttemptInfo attemptInfo, TimeSpan timeout, bool maskUnhandledException, AsyncCallback asyncCallback, object state)10     delegate IAsyncResult BeginSendHandler(MessageAttemptInfo attemptInfo, TimeSpan timeout, bool maskUnhandledException, AsyncCallback asyncCallback, object state);
EndSendHandler(IAsyncResult result)11     delegate void EndSendHandler(IAsyncResult result);
SendHandler(MessageAttemptInfo attemptInfo, TimeSpan timeout, bool maskUnhandledException)12     delegate void SendHandler(MessageAttemptInfo attemptInfo, TimeSpan timeout, bool maskUnhandledException);
ComponentFaultedHandler(Exception faultException, WsrmFault fault)13     delegate void ComponentFaultedHandler(Exception faultException, WsrmFault fault);
ComponentExceptionHandler(Exception exception)14     delegate void ComponentExceptionHandler(Exception exception);
RetryHandler(MessageAttemptInfo attemptInfo)15     delegate void RetryHandler(MessageAttemptInfo attemptInfo);
16 
17     sealed class ReliableOutputConnection
18     {
19         BeginSendHandler beginSendHandler;
20         OperationWithTimeoutBeginCallback beginSendAckRequestedHandler;
21         bool closed = false;
22         EndSendHandler endSendHandler;
23         OperationEndCallback endSendAckRequestedHandler;
24         UniqueId id;
25         MessageVersion messageVersion;
26         object mutex = new Object();
27         static AsyncCallback onSendRetriesComplete = Fx.ThunkCallback(new AsyncCallback(OnSendRetriesComplete));
28         static AsyncCallback onSendRetryComplete = Fx.ThunkCallback(new AsyncCallback(OnSendRetryComplete));
29         ReliableMessagingVersion reliableMessagingVersion;
30         Guard sendGuard = new Guard(Int32.MaxValue);
31         SendHandler sendHandler;
32         OperationWithTimeoutCallback sendAckRequestedHandler;
33         static Action<object> sendRetries = new Action<object>(SendRetries);
34         TimeSpan sendTimeout;
35         InterruptibleWaitObject shutdownHandle = new InterruptibleWaitObject(false);
36         TransmissionStrategy strategy;
37         bool terminated = false;
38 
ReliableOutputConnection(UniqueId id, int maxTransferWindowSize, MessageVersion messageVersion, ReliableMessagingVersion reliableMessagingVersion, TimeSpan initialRtt, bool requestAcks, TimeSpan sendTimeout)39         public ReliableOutputConnection(UniqueId id,
40             int maxTransferWindowSize,
41             MessageVersion messageVersion,
42             ReliableMessagingVersion reliableMessagingVersion,
43             TimeSpan initialRtt,
44             bool requestAcks,
45             TimeSpan sendTimeout)
46         {
47             this.id = id;
48             this.messageVersion = messageVersion;
49             this.reliableMessagingVersion = reliableMessagingVersion;
50             this.sendTimeout = sendTimeout;
51             this.strategy = new TransmissionStrategy(reliableMessagingVersion, initialRtt, maxTransferWindowSize,
52                 requestAcks, id);
53             this.strategy.RetryTimeoutElapsed = OnRetryTimeoutElapsed;
54             this.strategy.OnException = RaiseOnException;
55         }
56 
57         public ComponentFaultedHandler Faulted;
58         public ComponentExceptionHandler OnException;
59 
60         MessageVersion MessageVersion
61         {
62             get
63             {
64                 return this.messageVersion;
65             }
66         }
67 
68         public BeginSendHandler BeginSendHandler
69         {
70             set
71             {
72                 this.beginSendHandler = value;
73             }
74         }
75 
76         public OperationWithTimeoutBeginCallback BeginSendAckRequestedHandler
77         {
78             set
79             {
80                 this.beginSendAckRequestedHandler = value;
81             }
82         }
83 
84         public bool Closed
85         {
86             get
87             {
88                 return this.closed;
89             }
90         }
91 
92         public EndSendHandler EndSendHandler
93         {
94             set
95             {
96                 this.endSendHandler = value;
97             }
98         }
99 
100         public OperationEndCallback EndSendAckRequestedHandler
101         {
102             set
103             {
104                 this.endSendAckRequestedHandler = value;
105             }
106         }
107 
108         public Int64 Last
109         {
110             get
111             {
112                 return this.strategy.Last;
113             }
114         }
115 
116         public SendHandler SendHandler
117         {
118             set
119             {
120                 this.sendHandler = value;
121             }
122         }
123 
124         public OperationWithTimeoutCallback SendAckRequestedHandler
125         {
126             set
127             {
128                 this.sendAckRequestedHandler = value;
129             }
130         }
131 
132         public TransmissionStrategy Strategy
133         {
134             get
135             {
136                 return this.strategy;
137             }
138         }
139 
140         object ThisLock
141         {
142             get { return this.mutex; }
143         }
144 
Abort(ChannelBase channel)145         public void Abort(ChannelBase channel)
146         {
147             this.sendGuard.Abort();
148             this.shutdownHandle.Abort(channel);
149             this.strategy.Abort(channel);
150         }
151 
CompleteTransfer(TimeSpan timeout)152         void CompleteTransfer(TimeSpan timeout)
153         {
154             if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005)
155             {
156                 Message message = Message.CreateMessage(this.MessageVersion, WsrmFeb2005Strings.LastMessageAction);
157                 message.Properties.AllowOutputBatching = false;
158 
159                 // Return value ignored.
160                 this.InternalAddMessage(message, timeout, null, true);
161             }
162             else if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessaging11)
163             {
164                 if (this.strategy.SetLast())
165                 {
166                     this.shutdownHandle.Set();
167                 }
168                 else
169                 {
170                     this.sendAckRequestedHandler(timeout);
171                 }
172             }
173             else
174             {
175                 throw Fx.AssertAndThrow("Unsupported version.");
176             }
177         }
178 
AddMessage(Message message, TimeSpan timeout, object state)179         public bool AddMessage(Message message, TimeSpan timeout, object state)
180         {
181             return this.InternalAddMessage(message, timeout, state, false);
182         }
183 
BeginAddMessage(Message message, TimeSpan timeout, object state, AsyncCallback callback, object asyncState)184         public IAsyncResult BeginAddMessage(Message message, TimeSpan timeout, object state, AsyncCallback callback, object asyncState)
185         {
186             return new AddAsyncResult(message, false, timeout, state, this, callback, asyncState);
187         }
188 
EndAddMessage(IAsyncResult result)189         public bool EndAddMessage(IAsyncResult result)
190         {
191             return AddAsyncResult.End(result);
192         }
193 
BeginCompleteTransfer(TimeSpan timeout, AsyncCallback callback, object state)194         IAsyncResult BeginCompleteTransfer(TimeSpan timeout, AsyncCallback callback, object state)
195         {
196             if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005)
197             {
198                 Message message = Message.CreateMessage(this.MessageVersion, WsrmFeb2005Strings.LastMessageAction);
199                 message.Properties.AllowOutputBatching = false;
200                 return new AddAsyncResult(message, true, timeout, null, this, callback, state);
201             }
202             else if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessaging11)
203             {
204                 if (this.strategy.SetLast())
205                 {
206                     this.shutdownHandle.Set();
207                     return new AlreadyCompletedTransferAsyncResult(callback, state);
208                 }
209                 else
210                 {
211                     return this.beginSendAckRequestedHandler(timeout, callback, state);
212                 }
213             }
214             else
215             {
216                 throw Fx.AssertAndThrow("Unsupported version.");
217             }
218         }
219 
EndCompleteTransfer(IAsyncResult result)220         void EndCompleteTransfer(IAsyncResult result)
221         {
222             if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005)
223             {
224                 AddAsyncResult.End(result);
225             }
226             else if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessaging11)
227             {
228                 AlreadyCompletedTransferAsyncResult completedResult = result as AlreadyCompletedTransferAsyncResult;
229                 if (completedResult != null)
230                 {
231                     completedResult.End();
232                 }
233                 else
234                 {
235                     this.endSendAckRequestedHandler(result);
236                 }
237             }
238             else
239             {
240                 throw Fx.AssertAndThrow("Unsupported version.");
241             }
242         }
243 
BeginClose(TimeSpan timeout, AsyncCallback callback, object state)244         public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
245         {
246             bool completeTransfer = false;
247 
248             lock (this.ThisLock)
249             {
250                 completeTransfer = !this.closed;
251                 this.closed = true;
252             }
253 
254             OperationWithTimeoutBeginCallback[] beginCallbacks;
255             OperationEndCallback[] endCallbacks;
256 
257             beginCallbacks = new OperationWithTimeoutBeginCallback[] {
258                 completeTransfer ? this.BeginCompleteTransfer : default(OperationWithTimeoutBeginCallback),
259                 this.shutdownHandle.BeginWait,
260                 this.sendGuard.BeginClose,
261                 this.beginSendAckRequestedHandler };
262 
263             endCallbacks = new OperationEndCallback[] {
264                 completeTransfer ? this.EndCompleteTransfer : default(OperationEndCallback),
265                 this.shutdownHandle.EndWait,
266                 this.sendGuard.EndClose,
267                 this.endSendAckRequestedHandler };
268 
269             return OperationWithTimeoutComposer.BeginComposeAsyncOperations(timeout, beginCallbacks, endCallbacks, callback, state);
270         }
271 
CheckForTermination()272         public bool CheckForTermination()
273         {
274             return this.strategy.DoneTransmitting;
275         }
276 
Close(TimeSpan timeout)277         public void Close(TimeSpan timeout)
278         {
279             bool completeTransfer = false;
280 
281             lock (this.ThisLock)
282             {
283                 completeTransfer = !this.closed;
284                 this.closed = true;
285             }
286 
287             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
288 
289             if (completeTransfer)
290             {
291                 this.CompleteTransfer(timeoutHelper.RemainingTime());
292             }
293 
294             this.shutdownHandle.Wait(timeoutHelper.RemainingTime());
295             this.sendGuard.Close(timeoutHelper.RemainingTime());
296             this.strategy.Close();
297         }
298 
CompleteSendRetries(IAsyncResult result)299         void CompleteSendRetries(IAsyncResult result)
300         {
301             while (true)
302             {
303                 this.endSendHandler(result);
304                 this.sendGuard.Exit();
305                 this.strategy.DequeuePending();
306 
307                 if (this.sendGuard.Enter())
308                 {
309                     MessageAttemptInfo attemptInfo = this.strategy.GetMessageInfoForRetry(true);
310 
311                     if (attemptInfo.Message == null)
312                     {
313                         break;
314                     }
315                     else
316                     {
317                         result = this.beginSendHandler(attemptInfo, this.sendTimeout, true, onSendRetriesComplete, this);
318                         if (!result.CompletedSynchronously)
319                         {
320                             return;
321                         }
322                     }
323                 }
324                 else
325                 {
326                     return;
327                 }
328             }
329 
330             // We are here if there are no more messages to retry.
331             this.sendGuard.Exit();
332             this.OnTransferComplete();
333         }
334 
CompleteSendRetry(IAsyncResult result)335         void CompleteSendRetry(IAsyncResult result)
336         {
337             try
338             {
339                 this.endSendHandler(result);
340             }
341             finally
342             {
343                 this.sendGuard.Exit();
344             }
345         }
346 
EndClose(IAsyncResult result)347         public void EndClose(IAsyncResult result)
348         {
349             OperationWithTimeoutComposer.EndComposeAsyncOperations(result);
350             this.strategy.Close();
351         }
352 
Fault(ChannelBase channel)353         public void Fault(ChannelBase channel)
354         {
355             this.sendGuard.Abort();
356             this.shutdownHandle.Fault(channel);
357             this.strategy.Fault(channel);
358         }
359 
InternalAddMessage(Message message, TimeSpan timeout, object state, bool isLast)360         bool InternalAddMessage(Message message, TimeSpan timeout, object state, bool isLast)
361         {
362             MessageAttemptInfo attemptInfo;
363             TimeoutHelper helper = new TimeoutHelper(timeout);
364 
365             try
366             {
367                 if (isLast)
368                 {
369                     if (state != null)
370                     {
371                         throw Fx.AssertAndThrow("The isLast overload does not take a state.");
372                     }
373 
374                     attemptInfo = this.strategy.AddLast(message, helper.RemainingTime(), null);
375                 }
376                 else if (!this.strategy.Add(message, helper.RemainingTime(), state, out attemptInfo))
377                 {
378                     return false;
379                 }
380             }
381             catch (TimeoutException)
382             {
383                 if (isLast)
384                     this.RaiseFault(null, SequenceTerminatedFault.CreateCommunicationFault(this.id, SR.GetString(SR.SequenceTerminatedAddLastToWindowTimedOut), null));
385                 // else - RM does not fault the channel based on a timeout exception trying to add a sequenced message to the window.
386 
387                 throw;
388             }
389             catch (Exception e)
390             {
391                 if (!Fx.IsFatal(e))
392                     this.RaiseFault(null, SequenceTerminatedFault.CreateCommunicationFault(this.id, SR.GetString(SR.SequenceTerminatedUnknownAddToWindowError), null));
393 
394                 throw;
395             }
396 
397             if (sendGuard.Enter())
398             {
399                 try
400                 {
401                     this.sendHandler(attemptInfo, helper.RemainingTime(), false);
402                 }
403                 catch (QuotaExceededException)
404                 {
405                     this.RaiseFault(null, SequenceTerminatedFault.CreateQuotaExceededFault(this.id));
406                     throw;
407                 }
408                 finally
409                 {
410                     this.sendGuard.Exit();
411                 }
412             }
413 
414             return true;
415         }
416 
IsFinalAckConsistent(SequenceRangeCollection ranges)417         public bool IsFinalAckConsistent(SequenceRangeCollection ranges)
418         {
419             return this.strategy.IsFinalAckConsistent(ranges);
420         }
421 
OnRetryTimeoutElapsed(MessageAttemptInfo attemptInfo)422         void OnRetryTimeoutElapsed(MessageAttemptInfo attemptInfo)
423         {
424             if (this.sendGuard.Enter())
425             {
426                 IAsyncResult result = this.beginSendHandler(attemptInfo, this.sendTimeout, true, onSendRetryComplete, this);
427 
428                 if (result.CompletedSynchronously)
429                 {
430                     this.CompleteSendRetry(result);
431                 }
432             }
433         }
434 
OnSendRetryComplete(IAsyncResult result)435         static void OnSendRetryComplete(IAsyncResult result)
436         {
437             if (!result.CompletedSynchronously)
438             {
439                 ReliableOutputConnection outputConnection = (ReliableOutputConnection)result.AsyncState;
440 
441                 try
442                 {
443                     outputConnection.CompleteSendRetry(result);
444                 }
445 #pragma warning suppress 56500 // covered by FxCOP
446                 catch (Exception e)
447                 {
448                     if (Fx.IsFatal(e))
449                         throw;
450 
451                     outputConnection.RaiseOnException(e);
452                 }
453             }
454         }
455 
OnSendRetriesComplete(IAsyncResult result)456         static void OnSendRetriesComplete(IAsyncResult result)
457         {
458             if (!result.CompletedSynchronously)
459             {
460                 ReliableOutputConnection outputConnection = (ReliableOutputConnection)result.AsyncState;
461 
462                 try
463                 {
464                     outputConnection.CompleteSendRetries(result);
465                 }
466 #pragma warning suppress 56500 // covered by FxCOP
467                 catch (Exception e)
468                 {
469                     if (Fx.IsFatal(e))
470                         throw;
471 
472                     outputConnection.RaiseOnException(e);
473                 }
474             }
475         }
476 
OnTransferComplete()477         void OnTransferComplete()
478         {
479             this.strategy.DequeuePending();
480 
481             if (this.strategy.DoneTransmitting)
482                 Terminate();
483         }
484 
ProcessTransferred(Int64 transferred, SequenceRangeCollection ranges, int quotaRemaining)485         public void ProcessTransferred(Int64 transferred, SequenceRangeCollection ranges, int quotaRemaining)
486         {
487             if (transferred < 0)
488             {
489                 throw Fx.AssertAndThrow("Argument transferred must be a valid sequence number or 0 for protocol messages.");
490             }
491 
492             bool invalidAck;
493 
494             // ignored, TransmissionStrategy is being used to keep track of what must be re-sent.
495             // In the Request-Reply case this state may not align with acks.
496             bool inconsistentAck;
497 
498             this.strategy.ProcessAcknowledgement(ranges, out invalidAck, out inconsistentAck);
499             invalidAck = (invalidAck || ((transferred != 0) && !ranges.Contains(transferred)));
500 
501             if (!invalidAck)
502             {
503                 if ((transferred > 0) && this.strategy.ProcessTransferred(transferred, quotaRemaining))
504                 {
505                     ActionItem.Schedule(sendRetries, this);
506                 }
507                 else
508                 {
509                     this.OnTransferComplete();
510                 }
511             }
512             else
513             {
514                 WsrmFault fault = new InvalidAcknowledgementFault(this.id, ranges);
515                 RaiseFault(fault.CreateException(), fault);
516             }
517         }
518 
ProcessTransferred(SequenceRangeCollection ranges, int quotaRemaining)519         public void ProcessTransferred(SequenceRangeCollection ranges, int quotaRemaining)
520         {
521             bool invalidAck;
522             bool inconsistentAck;
523 
524             this.strategy.ProcessAcknowledgement(ranges, out invalidAck, out inconsistentAck);
525 
526             if (!invalidAck && !inconsistentAck)
527             {
528                 if (this.strategy.ProcessTransferred(ranges, quotaRemaining))
529                 {
530                     ActionItem.Schedule(sendRetries, this);
531                 }
532                 else
533                 {
534                     this.OnTransferComplete();
535                 }
536             }
537             else
538             {
539                 WsrmFault fault = new InvalidAcknowledgementFault(this.id, ranges);
540                 RaiseFault(fault.CreateException(), fault);
541             }
542         }
543 
RaiseFault(Exception faultException, WsrmFault fault)544         void RaiseFault(Exception faultException, WsrmFault fault)
545         {
546             ComponentFaultedHandler handler = this.Faulted;
547 
548             if (handler != null)
549                 handler(faultException, fault);
550         }
551 
RaiseOnException(Exception exception)552         void RaiseOnException(Exception exception)
553         {
554             ComponentExceptionHandler handler = this.OnException;
555 
556             if (handler != null)
557                 handler(exception);
558         }
559 
SendRetries()560         void SendRetries()
561         {
562             IAsyncResult result = null;
563 
564             if (this.sendGuard.Enter())
565             {
566                 MessageAttemptInfo attemptInfo = this.strategy.GetMessageInfoForRetry(false);
567 
568                 if (attemptInfo.Message != null)
569                 {
570                     result = this.beginSendHandler(attemptInfo, this.sendTimeout, true, onSendRetriesComplete, this);
571                 }
572 
573                 if (result != null)
574                 {
575                     if (result.CompletedSynchronously)
576                     {
577                         this.CompleteSendRetries(result);
578                     }
579                 }
580                 else
581                 {
582                     this.sendGuard.Exit();
583                     this.OnTransferComplete();
584                 }
585             }
586         }
587 
SendRetries(object state)588         static void SendRetries(object state)
589         {
590             ReliableOutputConnection outputConnection = (ReliableOutputConnection)state;
591 
592             try
593             {
594                 outputConnection.SendRetries();
595             }
596 #pragma warning suppress 56500 // covered by FxCOP
597             catch (Exception e)
598             {
599                 if (Fx.IsFatal(e))
600                     throw;
601 
602                 outputConnection.RaiseOnException(e);
603             }
604         }
605 
Terminate()606         public void Terminate()
607         {
608             lock (this.ThisLock)
609             {
610                 if (this.terminated)
611                     return;
612 
613                 this.terminated = true;
614             }
615 
616             this.shutdownHandle.Set();
617         }
618 
619         sealed class AddAsyncResult : AsyncResult
620         {
621             static AsyncCallback addCompleteStatic = Fx.ThunkCallback(new AsyncCallback(AddComplete));
622             ReliableOutputConnection connection;
623             bool isLast;
624             static AsyncCallback sendCompleteStatic = Fx.ThunkCallback(new AsyncCallback(SendComplete));
625             TimeoutHelper timeoutHelper;
626             bool validAdd;
627 
AddAsyncResult(Message message, bool isLast, TimeSpan timeout, object state, ReliableOutputConnection connection, AsyncCallback callback, object asyncState)628             public AddAsyncResult(Message message, bool isLast, TimeSpan timeout, object state,
629                 ReliableOutputConnection connection, AsyncCallback callback, object asyncState)
630                 : base(callback, asyncState)
631             {
632                 this.connection = connection;
633                 this.timeoutHelper = new TimeoutHelper(timeout);
634                 this.isLast = isLast;
635 
636                 bool complete = false;
637                 IAsyncResult result;
638 
639                 try
640                 {
641                     if (isLast)
642                     {
643                         if (state != null)
644                         {
645                             throw Fx.AssertAndThrow("The isLast overload does not take a state.");
646                         }
647 
648                         result = this.connection.strategy.BeginAddLast(message, this.timeoutHelper.RemainingTime(), state, addCompleteStatic, this);
649                     }
650                     else
651                     {
652                         result = this.connection.strategy.BeginAdd(message, this.timeoutHelper.RemainingTime(), state, addCompleteStatic, this);
653                     }
654                 }
655                 catch (TimeoutException)
656                 {
657                     if (isLast)
658                         this.connection.RaiseFault(null, SequenceTerminatedFault.CreateCommunicationFault(this.connection.id, SR.GetString(SR.SequenceTerminatedAddLastToWindowTimedOut), null));
659                     // else - RM does not fault the channel based on a timeout exception trying to add a sequenced message to the window.
660 
661                     throw;
662                 }
663                 catch (Exception e)
664                 {
665                     if (!Fx.IsFatal(e))
666                         this.connection.RaiseFault(null, SequenceTerminatedFault.CreateCommunicationFault(this.connection.id, SR.GetString(SR.SequenceTerminatedUnknownAddToWindowError), null));
667 
668                     throw;
669                 }
670 
671                 if (result.CompletedSynchronously)
672                     complete = this.CompleteAdd(result);
673 
674                 if (complete)
675                     this.Complete(true);
676             }
677 
AddComplete(IAsyncResult result)678             static void AddComplete(IAsyncResult result)
679             {
680                 if (!result.CompletedSynchronously)
681                 {
682                     AddAsyncResult addResult = (AddAsyncResult)result.AsyncState;
683                     bool complete = false;
684                     Exception completeException = null;
685 
686                     try
687                     {
688                         complete = addResult.CompleteAdd(result);
689                     }
690 #pragma warning suppress 56500 // covered by FxCOP
691                     catch (Exception e)
692                     {
693                         if (Fx.IsFatal(e))
694                             throw;
695 
696                         completeException = e;
697                     }
698 
699                     if (complete || completeException != null)
700                         addResult.Complete(false, completeException);
701                 }
702             }
703 
CompleteAdd(IAsyncResult result)704             bool CompleteAdd(IAsyncResult result)
705             {
706                 MessageAttemptInfo attemptInfo = default(MessageAttemptInfo);
707                 this.validAdd = true;
708 
709                 try
710                 {
711                     if (this.isLast)
712                     {
713                         attemptInfo = this.connection.strategy.EndAddLast(result);
714                     }
715                     else if (!this.connection.strategy.EndAdd(result, out attemptInfo))
716                     {
717                         this.validAdd = false;
718                         return true;
719                     }
720                 }
721                 catch (TimeoutException)
722                 {
723                     if (this.isLast)
724                         this.connection.RaiseFault(null, SequenceTerminatedFault.CreateCommunicationFault(this.connection.id, SR.GetString(SR.SequenceTerminatedAddLastToWindowTimedOut), null));
725                     // else - RM does not fault the channel based on a timeout exception trying to add a sequenced message to the window.
726 
727                     throw;
728                 }
729                 catch (Exception e)
730                 {
731                     if (!Fx.IsFatal(e))
732                         this.connection.RaiseFault(null, SequenceTerminatedFault.CreateCommunicationFault(this.connection.id, SR.GetString(SR.SequenceTerminatedUnknownAddToWindowError), null));
733 
734                     throw;
735                 }
736 
737                 if (this.connection.sendGuard.Enter())
738                 {
739                     bool throwing = true;
740 
741                     try
742                     {
743                         result = this.connection.beginSendHandler(attemptInfo, this.timeoutHelper.RemainingTime(), false, sendCompleteStatic, this);
744                         throwing = false;
745                     }
746                     catch (QuotaExceededException)
747                     {
748                         this.connection.RaiseFault(null, SequenceTerminatedFault.CreateQuotaExceededFault(this.connection.id));
749                         throw;
750                     }
751                     finally
752                     {
753                         if (throwing)
754                             this.connection.sendGuard.Exit();
755                     }
756                 }
757                 else
758                 {
759                     return true;
760                 }
761 
762                 if (result.CompletedSynchronously)
763                 {
764                     this.CompleteSend(result);
765                     return true;
766                 }
767                 else
768                 {
769                     return false;
770                 }
771             }
772 
CompleteSend(IAsyncResult result)773             void CompleteSend(IAsyncResult result)
774             {
775                 try
776                 {
777                     this.connection.endSendHandler(result);
778                 }
779                 catch (QuotaExceededException)
780                 {
781                     this.connection.RaiseFault(null, SequenceTerminatedFault.CreateQuotaExceededFault(this.connection.id));
782                     throw;
783                 }
784                 finally
785                 {
786                     this.connection.sendGuard.Exit();
787                 }
788             }
789 
SendComplete(IAsyncResult result)790             static void SendComplete(IAsyncResult result)
791             {
792                 if (!result.CompletedSynchronously)
793                 {
794                     AddAsyncResult addResult = (AddAsyncResult)result.AsyncState;
795                     Exception completeException = null;
796 
797                     try
798                     {
799                         addResult.CompleteSend(result);
800                     }
801 #pragma warning suppress 56500 // covered by FxCOP
802                     catch (Exception e)
803                     {
804                         if (Fx.IsFatal(e))
805                             throw;
806 
807                         completeException = e;
808                     }
809 
810                     addResult.Complete(false, completeException);
811                 }
812             }
813 
End(IAsyncResult result)814             public static bool End(IAsyncResult result)
815             {
816                 AsyncResult.End<AddAsyncResult>(result);
817                 return ((AddAsyncResult)result).validAdd;
818             }
819         }
820 
821         class AlreadyCompletedTransferAsyncResult : CompletedAsyncResult
822         {
AlreadyCompletedTransferAsyncResult(AsyncCallback callback, object state)823             public AlreadyCompletedTransferAsyncResult(AsyncCallback callback, object state)
824                 : base(callback, state)
825             {
826             }
827 
End()828             public void End()
829             {
830                 AsyncResult.End<AlreadyCompletedTransferAsyncResult>(this);
831             }
832         }
833     }
834 }
835