1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.ServiceModel.Channels
5 {
6     using System.Collections.Generic;
7     using System.ComponentModel;
8     using System.Diagnostics;
9     using System.Runtime;
10     using System.ServiceModel;
11     using System.ServiceModel.Diagnostics;
12     using System.ServiceModel.Diagnostics.Application;
13 
14     public abstract class CommunicationObject : ICommunicationObject
15     {
16         bool aborted;
17         bool closeCalled;
18 #if DEBUG
19         StackTrace closeStack;
20         StackTrace faultedStack;
21 #endif
22         ExceptionQueue exceptionQueue;
23         object mutex;
24         bool onClosingCalled;
25         bool onClosedCalled;
26         bool onOpeningCalled;
27         bool onOpenedCalled;
28         bool raisedClosed;
29         bool raisedClosing;
30         bool raisedFaulted;
31         bool traceOpenAndClose;
32         object eventSender;
33         CommunicationState state;
34 
CommunicationObject()35         protected CommunicationObject()
36             : this(new object())
37         {
38         }
39 
CommunicationObject(object mutex)40         protected CommunicationObject(object mutex)
41         {
42             this.mutex = mutex;
43             this.eventSender = this;
44             this.state = CommunicationState.Created;
45         }
46 
CommunicationObject(object mutex, object eventSender)47         internal CommunicationObject(object mutex, object eventSender)
48         {
49             this.mutex = mutex;
50             this.eventSender = eventSender;
51             this.state = CommunicationState.Created;
52         }
53 
54         internal bool Aborted
55         {
56             get { return this.aborted; }
57         }
58 
59         internal object EventSender
60         {
61             get { return this.eventSender; }
62             set { eventSender = value; }
63         }
64 
65         protected bool IsDisposed
66         {
67             get { return this.state == CommunicationState.Closed; }
68         }
69 
70         public CommunicationState State
71         {
72             get { return this.state; }
73         }
74 
75         protected object ThisLock
76         {
77             get { return this.mutex; }
78         }
79 
80         protected abstract TimeSpan DefaultCloseTimeout { get; }
81         protected abstract TimeSpan DefaultOpenTimeout { get; }
82 
83         internal TimeSpan InternalCloseTimeout
84         {
85             get { return this.DefaultCloseTimeout; }
86         }
87 
88         internal TimeSpan InternalOpenTimeout
89         {
90             get { return this.DefaultOpenTimeout; }
91         }
92 
93         public event EventHandler Closed;
94         public event EventHandler Closing;
95         public event EventHandler Faulted;
96         public event EventHandler Opened;
97         public event EventHandler Opening;
98 
Abort()99         public void Abort()
100         {
101             lock (ThisLock)
102             {
103                 if (this.aborted || this.state == CommunicationState.Closed)
104                     return;
105                 this.aborted = true;
106 #if DEBUG
107                 if (closeStack == null)
108                     closeStack = new StackTrace();
109 #endif
110 
111                 this.state = CommunicationState.Closing;
112             }
113 
114             if (DiagnosticUtility.ShouldTraceInformation)
115             {
116                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.CommunicationObjectAborted, SR.GetString(SR.TraceCodeCommunicationObjectAborted, TraceUtility.CreateSourceString(this)), this);
117             }
118 
119             bool throwing = true;
120 
121             try
122             {
123                 OnClosing();
124                 if (!this.onClosingCalled)
125                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
126 
127                 OnAbort();
128 
129                 OnClosed();
130                 if (!this.onClosedCalled)
131                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
132 
133                 throwing = false;
134             }
135             finally
136             {
137                 if (throwing)
138                 {
139                     if (DiagnosticUtility.ShouldTraceWarning)
140                         TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectAbortFailed, SR.GetString(SR.TraceCodeCommunicationObjectAbortFailed, this.GetCommunicationObjectType().ToString()), this);
141                 }
142             }
143         }
144 
BeginClose(AsyncCallback callback, object state)145         public IAsyncResult BeginClose(AsyncCallback callback, object state)
146         {
147             return this.BeginClose(this.DefaultCloseTimeout, callback, state);
148         }
149 
BeginClose(TimeSpan timeout, AsyncCallback callback, object state)150         public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
151         {
152             if (timeout < TimeSpan.Zero)
153                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
154                     new ArgumentOutOfRangeException("timeout", SR.GetString(SR.SFxTimeoutOutOfRange0)));
155 
156             using (DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose ? this.CreateCloseActivity() : null)
157             {
158                 CommunicationState originalState;
159                 lock (ThisLock)
160                 {
161                     originalState = this.state;
162 #if DEBUG
163                     if (closeStack == null)
164                         closeStack = new StackTrace();
165 #endif
166                     if (originalState != CommunicationState.Closed)
167                         this.state = CommunicationState.Closing;
168 
169                     this.closeCalled = true;
170                 }
171 
172                 switch (originalState)
173                 {
174                     case CommunicationState.Created:
175                     case CommunicationState.Opening:
176                     case CommunicationState.Faulted:
177                         this.Abort();
178                         if (originalState == CommunicationState.Faulted)
179                         {
180                             throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
181                         }
182                         return new AlreadyClosedAsyncResult(callback, state);
183 
184                     case CommunicationState.Opened:
185                         {
186                             bool throwing = true;
187                             try
188                             {
189                                 OnClosing();
190                                 if (!this.onClosingCalled)
191                                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
192 
193                                 IAsyncResult result = new CloseAsyncResult(this, timeout, callback, state);
194                                 throwing = false;
195                                 return result;
196                             }
197                             finally
198                             {
199                                 if (throwing)
200                                 {
201                                     if (DiagnosticUtility.ShouldTraceWarning)
202                                     {
203                                         TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectCloseFailed, SR.GetString(SR.TraceCodeCommunicationObjectCloseFailed, this.GetCommunicationObjectType().ToString()), this);
204                                     }
205 
206                                     Abort();
207                                 }
208                             }
209                         }
210 
211                     case CommunicationState.Closing:
212                     case CommunicationState.Closed:
213                         return new AlreadyClosedAsyncResult(callback, state);
214 
215                     default:
216                         throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState");
217                 }
218             }
219         }
220 
BeginOpen(AsyncCallback callback, object state)221         public IAsyncResult BeginOpen(AsyncCallback callback, object state)
222         {
223             return this.BeginOpen(this.DefaultOpenTimeout, callback, state);
224         }
225 
BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)226         public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
227         {
228             if (timeout < TimeSpan.Zero)
229                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
230                     new ArgumentOutOfRangeException("timeout", SR.GetString(SR.SFxTimeoutOutOfRange0)));
231 
232             lock (ThisLock)
233             {
234                 ThrowIfDisposedOrImmutable();
235                 this.state = CommunicationState.Opening;
236             }
237 
238             bool throwing = true;
239             try
240             {
241                 OnOpening();
242                 if (!this.onOpeningCalled)
243                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnOpening"), Guid.Empty, this);
244 
245                 IAsyncResult result = new OpenAsyncResult(this, timeout, callback, state);
246                 throwing = false;
247                 return result;
248             }
249             finally
250             {
251                 if (throwing)
252                 {
253                     if (DiagnosticUtility.ShouldTraceWarning)
254                     {
255                         TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectOpenFailed, SR.GetString(SR.TraceCodeCommunicationObjectOpenFailed, this.GetCommunicationObjectType().ToString()), this);
256                     }
257 
258                     Fault();
259                 }
260             }
261         }
262 
Close()263         public void Close()
264         {
265             this.Close(this.DefaultCloseTimeout);
266         }
267 
Close(TimeSpan timeout)268         public void Close(TimeSpan timeout)
269         {
270             if (timeout < TimeSpan.Zero)
271                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
272                     new ArgumentOutOfRangeException("timeout", SR.GetString(SR.SFxTimeoutOutOfRange0)));
273 
274             using (DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose ? this.CreateCloseActivity() : null)
275             {
276 
277                 CommunicationState originalState;
278                 lock (ThisLock)
279                 {
280                     originalState = this.state;
281 #if DEBUG
282                     if (closeStack == null)
283                         closeStack = new StackTrace();
284 #endif
285                     if (originalState != CommunicationState.Closed)
286                         this.state = CommunicationState.Closing;
287 
288                     this.closeCalled = true;
289                 }
290 
291                 switch (originalState)
292                 {
293                     case CommunicationState.Created:
294                     case CommunicationState.Opening:
295                     case CommunicationState.Faulted:
296                         this.Abort();
297                         if (originalState == CommunicationState.Faulted)
298                         {
299                             throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
300                         }
301                         break;
302 
303                     case CommunicationState.Opened:
304                         {
305                             bool throwing = true;
306                             try
307                             {
308                                 TimeoutHelper actualTimeout = new TimeoutHelper(timeout);
309 
310                                 OnClosing();
311                                 if (!this.onClosingCalled)
312                                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
313 
314                                 OnClose(actualTimeout.RemainingTime());
315 
316                                 OnClosed();
317                                 if (!this.onClosedCalled)
318                                     throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
319 
320                                 throwing = false;
321                             }
322                             finally
323                             {
324                                 if (throwing)
325                                 {
326                                     if (DiagnosticUtility.ShouldTraceWarning)
327                                     {
328                                         TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectCloseFailed, SR.GetString(SR.TraceCodeCommunicationObjectCloseFailed, this.GetCommunicationObjectType().ToString()), this);
329                                     }
330 
331                                     Abort();
332                                 }
333                             }
334                             break;
335                         }
336 
337                     case CommunicationState.Closing:
338                     case CommunicationState.Closed:
339                         break;
340 
341                     default:
342                         throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState");
343                 }
344             }
345         }
346 
CreateNotOpenException()347         Exception CreateNotOpenException()
348         {
349             return new InvalidOperationException(SR.GetString(SR.CommunicationObjectCannotBeUsed, this.GetCommunicationObjectType().ToString(), this.state.ToString()));
350         }
351 
CreateImmutableException()352         Exception CreateImmutableException()
353         {
354             return new InvalidOperationException(SR.GetString(SR.CommunicationObjectCannotBeModifiedInState, this.GetCommunicationObjectType().ToString(), this.state.ToString()));
355         }
356 
CreateBaseClassMethodNotCalledException(string method)357         Exception CreateBaseClassMethodNotCalledException(string method)
358         {
359             return new InvalidOperationException(SR.GetString(SR.CommunicationObjectBaseClassMethodNotCalled, this.GetCommunicationObjectType().ToString(), method));
360         }
361 
CreateClosedException()362         internal Exception CreateClosedException()
363         {
364             if (!this.closeCalled)
365             {
366                 return CreateAbortedException();
367             }
368             else
369             {
370 #if DEBUG
371                 string originalStack = closeStack.ToString().Replace("\r\n", "\r\n    ");
372                 return new ObjectDisposedException(this.GetCommunicationObjectType().ToString() + ", Object already closed:\r\n    " + originalStack);
373 #else
374                 return new ObjectDisposedException(this.GetCommunicationObjectType().ToString());
375 #endif
376             }
377         }
378 
CreateFaultedException()379         internal Exception CreateFaultedException()
380         {
381 #if DEBUG
382             string originalStack = faultedStack.ToString().Replace("\r\n", "\r\n    ");
383             string message = SR.GetString(SR.CommunicationObjectFaultedStack2, this.GetCommunicationObjectType().ToString(), originalStack);
384 #else
385             string message = SR.GetString(SR.CommunicationObjectFaulted1, this.GetCommunicationObjectType().ToString());
386 #endif
387             return new CommunicationObjectFaultedException(message);
388         }
389 
CreateAbortedException()390         internal Exception CreateAbortedException()
391         {
392 #if DEBUG
393             string originalStack = closeStack.ToString().Replace("\r\n", "\r\n    ");
394             return new CommunicationObjectAbortedException(SR.GetString(SR.CommunicationObjectAbortedStack2, this.GetCommunicationObjectType().ToString(), originalStack));
395 #else
396             return new CommunicationObjectAbortedException(SR.GetString(SR.CommunicationObjectAborted1, this.GetCommunicationObjectType().ToString()));
397 #endif
398         }
399 
400         internal virtual string CloseActivityName
401         {
402             get { return SR.GetString(SR.ActivityClose, this.GetType().FullName); }
403         }
404 
405         internal virtual string OpenActivityName
406         {
407             get { return SR.GetString(SR.ActivityOpen, this.GetType().FullName); }
408         }
409 
410         internal virtual ActivityType OpenActivityType
411         {
412             get { return ActivityType.Open; }
413         }
414 
CreateCloseActivity()415         ServiceModelActivity CreateCloseActivity()
416         {
417             ServiceModelActivity retval = null;
418             retval = ServiceModelActivity.CreateBoundedActivity();
419             if (DiagnosticUtility.ShouldUseActivity)
420             {
421                 ServiceModelActivity.Start(retval, this.CloseActivityName, ActivityType.Close);
422             }
423 
424             return retval;
425         }
426 
DoneReceivingInCurrentState()427         internal bool DoneReceivingInCurrentState()
428         {
429             this.ThrowPending();
430 
431             switch (this.state)
432             {
433                 case CommunicationState.Created:
434                     throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
435 
436                 case CommunicationState.Opening:
437                     throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
438 
439                 case CommunicationState.Opened:
440                     return false;
441 
442                 case CommunicationState.Closing:
443                     return true;
444 
445                 case CommunicationState.Closed:
446                     return true;
447 
448                 case CommunicationState.Faulted:
449                     return true;
450 
451                 default:
452                     throw Fx.AssertAndThrow("DoneReceivingInCurrentState: Unknown CommunicationObject.state");
453             }
454         }
455 
EndClose(IAsyncResult result)456         public void EndClose(IAsyncResult result)
457         {
458             if (result is AlreadyClosedAsyncResult)
459                 AlreadyClosedAsyncResult.End(result);
460             else
461                 CloseAsyncResult.End(result);
462         }
463 
EndOpen(IAsyncResult result)464         public void EndOpen(IAsyncResult result)
465         {
466             OpenAsyncResult.End(result);
467         }
468 
Fault()469         protected void Fault()
470         {
471             lock (ThisLock)
472             {
473                 if (this.state == CommunicationState.Closed || this.state == CommunicationState.Closing)
474                     return;
475 
476                 if (this.state == CommunicationState.Faulted)
477                     return;
478 #if DEBUG
479                 if (faultedStack == null)
480                     faultedStack = new StackTrace();
481 #endif
482                 this.state = CommunicationState.Faulted;
483             }
484 
485             OnFaulted();
486         }
487 
Fault(Exception exception)488         internal void Fault(Exception exception)
489         {
490             lock (this.ThisLock)
491             {
492                 if (this.exceptionQueue == null)
493                     this.exceptionQueue = new ExceptionQueue(this.ThisLock);
494             }
495 
496             if (exception != null && DiagnosticUtility.ShouldTraceInformation)
497             {
498                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.CommunicationObjectFaultReason,
499                     SR.GetString(SR.TraceCodeCommunicationObjectFaultReason), exception, null);
500             }
501 
502             this.exceptionQueue.AddException(exception);
503             this.Fault();
504         }
505 
AddPendingException(Exception exception)506         internal void AddPendingException(Exception exception)
507         {
508             lock (this.ThisLock)
509             {
510                 if (this.exceptionQueue == null)
511                     this.exceptionQueue = new ExceptionQueue(this.ThisLock);
512             }
513 
514             this.exceptionQueue.AddException(exception);
515         }
516 
GetPendingException()517         internal Exception GetPendingException()
518         {
519             CommunicationState currentState = this.state;
520 
521             Fx.Assert(currentState == CommunicationState.Closing || currentState == CommunicationState.Closed || currentState == CommunicationState.Faulted,
522                 "CommunicationObject.GetPendingException(currentState == CommunicationState.Closing || currentState == CommunicationState.Closed || currentState == CommunicationState.Faulted)");
523 
524             ExceptionQueue queue = this.exceptionQueue;
525             if (queue != null)
526             {
527                 return queue.GetException();
528             }
529             else
530             {
531                 return null;
532             }
533         }
534 
535         // Terminal is loosely defined as an interruption to close or a fault.
GetTerminalException()536         internal Exception GetTerminalException()
537         {
538             Exception exception = this.GetPendingException();
539 
540             if (exception != null)
541             {
542                 return exception;
543             }
544 
545             switch (this.state)
546             {
547                 case CommunicationState.Closing:
548                 case CommunicationState.Closed:
549                     return new CommunicationException(SR.GetString(SR.CommunicationObjectCloseInterrupted1, this.GetCommunicationObjectType().ToString()));
550 
551                 case CommunicationState.Faulted:
552                     return this.CreateFaultedException();
553 
554                 default:
555                     throw Fx.AssertAndThrow("GetTerminalException: Invalid CommunicationObject.state");
556             }
557         }
558 
Open()559         public void Open()
560         {
561             this.Open(this.DefaultOpenTimeout);
562         }
563 
Open(TimeSpan timeout)564         public void Open(TimeSpan timeout)
565         {
566             if (timeout < TimeSpan.Zero)
567                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
568                     new ArgumentOutOfRangeException("timeout", SR.GetString(SR.SFxTimeoutOutOfRange0)));
569 
570             using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity && this.TraceOpenAndClose ? ServiceModelActivity.CreateBoundedActivity() : null)
571             {
572                 if (DiagnosticUtility.ShouldUseActivity)
573                 {
574                     ServiceModelActivity.Start(activity, this.OpenActivityName, this.OpenActivityType);
575                 }
576                 lock (ThisLock)
577                 {
578                     ThrowIfDisposedOrImmutable();
579                     this.state = CommunicationState.Opening;
580                 }
581 
582                 bool throwing = true;
583                 try
584                 {
585                     TimeoutHelper actualTimeout = new TimeoutHelper(timeout);
586 
587                     OnOpening();
588                     if (!this.onOpeningCalled)
589                         throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnOpening"), Guid.Empty, this);
590 
591                     OnOpen(actualTimeout.RemainingTime());
592 
593                     OnOpened();
594                     if (!this.onOpenedCalled)
595                         throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnOpened"), Guid.Empty, this);
596 
597                     throwing = false;
598                 }
599                 finally
600                 {
601                     if (throwing)
602                     {
603                         if (DiagnosticUtility.ShouldTraceWarning)
604                         {
605                             TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectOpenFailed, SR.GetString(SR.TraceCodeCommunicationObjectOpenFailed, this.GetCommunicationObjectType().ToString()), this);
606                         }
607 
608                         Fault();
609                     }
610                 }
611             }
612         }
613 
OnClosed()614         protected virtual void OnClosed()
615         {
616             this.onClosedCalled = true;
617 
618             lock (ThisLock)
619             {
620                 if (this.raisedClosed)
621                     return;
622                 this.raisedClosed = true;
623                 this.state = CommunicationState.Closed;
624             }
625 
626             if (DiagnosticUtility.ShouldTraceVerbose)
627             {
628                 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectClosed, SR.GetString(SR.TraceCodeCommunicationObjectClosed, TraceUtility.CreateSourceString(this)), this);
629             }
630 
631             EventHandler handler = Closed;
632             if (handler != null)
633             {
634                 try
635                 {
636                     handler(eventSender, EventArgs.Empty);
637                 }
638                 catch (Exception exception)
639                 {
640                     if (Fx.IsFatal(exception))
641                         throw;
642 
643                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
644                 }
645             }
646         }
647 
OnClosing()648         protected virtual void OnClosing()
649         {
650             this.onClosingCalled = true;
651 
652             lock (ThisLock)
653             {
654                 if (this.raisedClosing)
655                     return;
656                 this.raisedClosing = true;
657             }
658 
659             if (DiagnosticUtility.ShouldTraceVerbose)
660             {
661                 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectClosing, SR.GetString(SR.TraceCodeCommunicationObjectClosing, TraceUtility.CreateSourceString(this)), this);
662             }
663             EventHandler handler = Closing;
664             if (handler != null)
665             {
666                 try
667                 {
668                     handler(eventSender, EventArgs.Empty);
669                 }
670                 catch (Exception exception)
671                 {
672                     if (Fx.IsFatal(exception))
673                         throw;
674 
675                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
676                 }
677             }
678         }
679 
OnFaulted()680         protected virtual void OnFaulted()
681         {
682             lock (ThisLock)
683             {
684                 if (this.raisedFaulted)
685                     return;
686                 this.raisedFaulted = true;
687             }
688 
689             if (DiagnosticUtility.ShouldTraceWarning)
690             {
691                 TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectFaulted, SR.GetString(SR.TraceCodeCommunicationObjectFaulted, this.GetCommunicationObjectType().ToString()), this);
692             }
693 
694             EventHandler handler = Faulted;
695             if (handler != null)
696             {
697                 try
698                 {
699                     handler(eventSender, EventArgs.Empty);
700                 }
701                 catch (Exception exception)
702                 {
703                     if (Fx.IsFatal(exception))
704                         throw;
705 
706                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
707                 }
708             }
709         }
710 
OnOpened()711         protected virtual void OnOpened()
712         {
713             this.onOpenedCalled = true;
714 
715             lock (ThisLock)
716             {
717                 if (this.aborted || this.state != CommunicationState.Opening)
718                     return;
719                 this.state = CommunicationState.Opened;
720             }
721 
722             if (DiagnosticUtility.ShouldTraceVerbose)
723                 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectOpened, SR.GetString(SR.TraceCodeCommunicationObjectOpened, TraceUtility.CreateSourceString(this)), this);
724 
725             EventHandler handler = Opened;
726             if (handler != null)
727             {
728                 try
729                 {
730                     handler(eventSender, EventArgs.Empty);
731                 }
732                 catch (Exception exception)
733                 {
734                     if (Fx.IsFatal(exception))
735                         throw;
736 
737                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
738                 }
739             }
740         }
741 
OnOpening()742         protected virtual void OnOpening()
743         {
744             this.onOpeningCalled = true;
745 
746             if (DiagnosticUtility.ShouldTraceVerbose)
747             {
748                 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.CommunicationObjectOpening, SR.GetString(SR.TraceCodeCommunicationObjectOpening, TraceUtility.CreateSourceString(this)), this);
749             }
750 
751             EventHandler handler = Opening;
752             if (handler != null)
753             {
754                 try
755                 {
756                     handler(eventSender, EventArgs.Empty);
757                 }
758                 catch (Exception exception)
759                 {
760                     if (Fx.IsFatal(exception))
761                         throw;
762 
763                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
764                 }
765             }
766         }
767 
ThrowIfFaulted()768         internal void ThrowIfFaulted()
769         {
770             this.ThrowPending();
771 
772             switch (this.state)
773             {
774                 case CommunicationState.Created:
775                     break;
776 
777                 case CommunicationState.Opening:
778                     break;
779 
780                 case CommunicationState.Opened:
781                     break;
782 
783                 case CommunicationState.Closing:
784                     break;
785 
786                 case CommunicationState.Closed:
787                     break;
788 
789                 case CommunicationState.Faulted:
790                     throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
791 
792                 default:
793                     throw Fx.AssertAndThrow("ThrowIfFaulted: Unknown CommunicationObject.state");
794             }
795         }
796 
ThrowIfAborted()797         internal void ThrowIfAborted()
798         {
799             if (this.aborted && !this.closeCalled)
800             {
801                 throw TraceUtility.ThrowHelperError(CreateAbortedException(), Guid.Empty, this);
802             }
803         }
804 
805         internal bool TraceOpenAndClose
806         {
807             get
808             {
809                 return this.traceOpenAndClose;
810             }
811             set
812             {
813                 this.traceOpenAndClose = value && DiagnosticUtility.ShouldUseActivity;
814             }
815         }
816 
ThrowIfClosed()817         internal void ThrowIfClosed()
818         {
819             ThrowPending();
820 
821             switch (this.state)
822             {
823                 case CommunicationState.Created:
824                     break;
825 
826                 case CommunicationState.Opening:
827                     break;
828 
829                 case CommunicationState.Opened:
830                     break;
831 
832                 case CommunicationState.Closing:
833                     break;
834 
835                 case CommunicationState.Closed:
836                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
837 
838                 case CommunicationState.Faulted:
839                     throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
840 
841                 default:
842                     throw Fx.AssertAndThrow("ThrowIfClosed: Unknown CommunicationObject.state");
843             }
844         }
845 
GetCommunicationObjectType()846         protected virtual Type GetCommunicationObjectType()
847         {
848             return this.GetType();
849         }
850 
ThrowIfDisposed()851         protected internal void ThrowIfDisposed()
852         {
853             ThrowPending();
854 
855             switch (this.state)
856             {
857                 case CommunicationState.Created:
858                     break;
859 
860                 case CommunicationState.Opening:
861                     break;
862 
863                 case CommunicationState.Opened:
864                     break;
865 
866                 case CommunicationState.Closing:
867                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
868 
869                 case CommunicationState.Closed:
870                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
871 
872                 case CommunicationState.Faulted:
873                     throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
874 
875                 default:
876                     throw Fx.AssertAndThrow("ThrowIfDisposed: Unknown CommunicationObject.state");
877             }
878         }
879 
ThrowIfClosedOrOpened()880         internal void ThrowIfClosedOrOpened()
881         {
882             ThrowPending();
883 
884             switch (this.state)
885             {
886                 case CommunicationState.Created:
887                     break;
888 
889                 case CommunicationState.Opening:
890                     break;
891 
892                 case CommunicationState.Opened:
893                     throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
894 
895                 case CommunicationState.Closing:
896                     throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
897 
898                 case CommunicationState.Closed:
899                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
900 
901                 case CommunicationState.Faulted:
902                     throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
903 
904                 default:
905                     throw Fx.AssertAndThrow("ThrowIfClosedOrOpened: Unknown CommunicationObject.state");
906             }
907         }
908 
ThrowIfDisposedOrImmutable()909         protected internal void ThrowIfDisposedOrImmutable()
910         {
911             ThrowPending();
912 
913             switch (this.state)
914             {
915                 case CommunicationState.Created:
916                     break;
917 
918                 case CommunicationState.Opening:
919                     throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
920 
921                 case CommunicationState.Opened:
922                     throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
923 
924                 case CommunicationState.Closing:
925                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
926 
927                 case CommunicationState.Closed:
928                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
929 
930                 case CommunicationState.Faulted:
931                     throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
932 
933                 default:
934                     throw Fx.AssertAndThrow("ThrowIfDisposedOrImmutable: Unknown CommunicationObject.state");
935             }
936         }
937 
ThrowIfDisposedOrNotOpen()938         protected internal void ThrowIfDisposedOrNotOpen()
939         {
940             ThrowPending();
941 
942             switch (this.state)
943             {
944                 case CommunicationState.Created:
945                     throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
946 
947                 case CommunicationState.Opening:
948                     throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
949 
950                 case CommunicationState.Opened:
951                     break;
952 
953                 case CommunicationState.Closing:
954                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
955 
956                 case CommunicationState.Closed:
957                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
958 
959                 case CommunicationState.Faulted:
960                     throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
961 
962                 default:
963                     throw Fx.AssertAndThrow("ThrowIfDisposedOrNotOpen: Unknown CommunicationObject.state");
964             }
965         }
966 
ThrowIfNotOpened()967         internal void ThrowIfNotOpened()
968         {
969             if (this.state == CommunicationState.Created || this.state == CommunicationState.Opening)
970                 throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
971         }
972 
ThrowIfClosedOrNotOpen()973         internal void ThrowIfClosedOrNotOpen()
974         {
975             ThrowPending();
976 
977             switch (this.state)
978             {
979                 case CommunicationState.Created:
980                     throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
981 
982                 case CommunicationState.Opening:
983                     throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
984 
985                 case CommunicationState.Opened:
986                     break;
987 
988                 case CommunicationState.Closing:
989                     break;
990 
991                 case CommunicationState.Closed:
992                     throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
993 
994                 case CommunicationState.Faulted:
995                     throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
996 
997                 default:
998                     throw Fx.AssertAndThrow("ThrowIfClosedOrNotOpen: Unknown CommunicationObject.state");
999             }
1000         }
1001 
ThrowPending()1002         internal void ThrowPending()
1003         {
1004             ExceptionQueue queue = this.exceptionQueue;
1005 
1006             if (queue != null)
1007             {
1008                 Exception exception = queue.GetException();
1009 
1010                 if (exception != null)
1011                 {
1012                     throw TraceUtility.ThrowHelperError(exception, Guid.Empty, this);
1013                 }
1014             }
1015         }
1016 
1017         //
1018         // State callbacks
1019         //
1020 
OnAbort()1021         protected abstract void OnAbort();
1022 
OnClose(TimeSpan timeout)1023         protected abstract void OnClose(TimeSpan timeout);
OnEndClose(IAsyncResult result)1024         protected abstract void OnEndClose(IAsyncResult result);
OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)1025         protected abstract IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state);
1026 
OnOpen(TimeSpan timeout)1027         protected abstract void OnOpen(TimeSpan timeout);
OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)1028         protected abstract IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state);
OnEndOpen(IAsyncResult result)1029         protected abstract void OnEndOpen(IAsyncResult result);
1030 
1031         class AlreadyClosedAsyncResult : CompletedAsyncResult
1032         {
AlreadyClosedAsyncResult(AsyncCallback callback, object state)1033             public AlreadyClosedAsyncResult(AsyncCallback callback, object state)
1034                 : base(callback, state)
1035             {
1036             }
1037         }
1038 
1039         class ExceptionQueue
1040         {
1041             Queue<Exception> exceptions = new Queue<Exception>();
1042             object thisLock;
1043 
ExceptionQueue(object thisLock)1044             internal ExceptionQueue(object thisLock)
1045             {
1046                 this.thisLock = thisLock;
1047             }
1048 
1049             object ThisLock
1050             {
1051                 get { return this.thisLock; }
1052             }
1053 
AddException(Exception exception)1054             public void AddException(Exception exception)
1055             {
1056                 if (exception == null)
1057                 {
1058                     return;
1059                 }
1060 
1061                 lock (this.ThisLock)
1062                 {
1063                     this.exceptions.Enqueue(exception);
1064                 }
1065             }
1066 
GetException()1067             public Exception GetException()
1068             {
1069                 lock (this.ThisLock)
1070                 {
1071                     if (this.exceptions.Count > 0)
1072                     {
1073                         return this.exceptions.Dequeue();
1074                     }
1075                 }
1076 
1077                 return null;
1078             }
1079         }
1080 
1081         class OpenAsyncResult : AsyncResult
1082         {
1083             static AsyncCompletion onOpenCompletion = new AsyncCompletion(OnOpenCompletion);
1084 
1085             CommunicationObject communicationObject;
1086             TimeoutHelper timeout;
1087 
OpenAsyncResult(CommunicationObject communicationObject, TimeSpan timeout, AsyncCallback callback, object state)1088             public OpenAsyncResult(CommunicationObject communicationObject, TimeSpan timeout, AsyncCallback callback, object state)
1089                 : base(callback, state)
1090             {
1091                 this.communicationObject = communicationObject;
1092                 this.timeout = new TimeoutHelper(timeout);
1093 
1094                 base.OnCompleting = new Action<AsyncResult, Exception>(OnOpenCompleted);
1095 
1096                 if (InvokeOpen())
1097                 {
1098                     this.Complete(true);
1099                 }
1100             }
1101 
InvokeOpen()1102             bool InvokeOpen()
1103             {
1104                 IAsyncResult result = this.communicationObject.OnBeginOpen(this.timeout.RemainingTime(),
1105                     base.PrepareAsyncCompletion(onOpenCompletion), this);
1106                 if (result.CompletedSynchronously)
1107                 {
1108                     return OnOpenCompletion(result);
1109                 }
1110                 else
1111                 {
1112                     return false;
1113                 }
1114             }
1115 
NotifyOpened()1116             void NotifyOpened()
1117             {
1118                 this.communicationObject.OnOpened();
1119                 if (!this.communicationObject.onOpenedCalled)
1120                 {
1121                     throw TraceUtility.ThrowHelperError(
1122                         this.communicationObject.CreateBaseClassMethodNotCalledException("OnOpened"),
1123                         Guid.Empty, this.communicationObject);
1124                 }
1125             }
1126 
OnOpenCompleted(AsyncResult result, Exception exception)1127             void OnOpenCompleted(AsyncResult result, Exception exception)
1128             {
1129                 if (exception != null)
1130                 {
1131                     if (DiagnosticUtility.ShouldTraceWarning)
1132                     {
1133                         TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectOpenFailed,
1134                             SR.GetString(SR.TraceCodeCommunicationObjectOpenFailed, this.communicationObject.GetCommunicationObjectType().ToString()),
1135                             this, exception);
1136                     }
1137 
1138                     this.communicationObject.Fault();
1139                 }
1140             }
1141 
OnOpenCompletion(IAsyncResult result)1142             static bool OnOpenCompletion(IAsyncResult result)
1143             {
1144                 OpenAsyncResult thisPtr = (OpenAsyncResult)result.AsyncState;
1145                 thisPtr.communicationObject.OnEndOpen(result);
1146                 thisPtr.NotifyOpened();
1147                 return true;
1148             }
1149 
End(IAsyncResult result)1150             public static void End(IAsyncResult result)
1151             {
1152                 AsyncResult.End<OpenAsyncResult>(result);
1153             }
1154         }
1155 
1156         class CloseAsyncResult : TraceAsyncResult
1157         {
1158             static AsyncCompletion onCloseCompletion = new AsyncCompletion(OnCloseCompletion);
1159 
1160             CommunicationObject communicationObject;
1161             TimeoutHelper timeout;
1162 
CloseAsyncResult(CommunicationObject communicationObject, TimeSpan timeout, AsyncCallback callback, object state)1163             public CloseAsyncResult(CommunicationObject communicationObject, TimeSpan timeout, AsyncCallback callback, object state)
1164                 : base(callback, state)
1165             {
1166                 this.communicationObject = communicationObject;
1167                 this.timeout = new TimeoutHelper(timeout);
1168 
1169                 base.OnCompleting = new Action<AsyncResult, Exception>(OnCloseCompleted);
1170                 if (InvokeClose())
1171                 {
1172                     this.Complete(true);
1173                 }
1174             }
1175 
InvokeClose()1176             bool InvokeClose()
1177             {
1178                 IAsyncResult result = this.communicationObject.OnBeginClose(this.timeout.RemainingTime(),
1179                     base.PrepareAsyncCompletion(onCloseCompletion), this);
1180                 if (result.CompletedSynchronously)
1181                 {
1182                     return OnCloseCompletion(result);
1183                 }
1184                 else
1185                 {
1186                     return false;
1187                 }
1188             }
1189 
NotifyClosed()1190             void NotifyClosed()
1191             {
1192                 this.communicationObject.OnClosed();
1193                 if (!this.communicationObject.onClosedCalled)
1194                 {
1195                     throw TraceUtility.ThrowHelperError(
1196                         this.communicationObject.CreateBaseClassMethodNotCalledException("OnClosed"),
1197                         Guid.Empty, this.communicationObject);
1198                 }
1199             }
1200 
OnCloseCompleted(AsyncResult result, Exception exception)1201             void OnCloseCompleted(AsyncResult result, Exception exception)
1202             {
1203                 if (exception != null)
1204                 {
1205                     if (DiagnosticUtility.ShouldTraceWarning)
1206                     {
1207                         TraceUtility.TraceEvent(TraceEventType.Warning, TraceCode.CommunicationObjectCloseFailed,
1208                             SR.GetString(SR.TraceCodeCommunicationObjectCloseFailed, this.communicationObject.GetCommunicationObjectType().ToString()),
1209                             this, exception);
1210                     }
1211 
1212                     this.communicationObject.Abort();
1213                 }
1214             }
1215 
OnCloseCompletion(IAsyncResult result)1216             static bool OnCloseCompletion(IAsyncResult result)
1217             {
1218                 CloseAsyncResult thisPtr = (CloseAsyncResult)result.AsyncState;
1219                 thisPtr.communicationObject.OnEndClose(result);
1220                 thisPtr.NotifyClosed();
1221                 return true;
1222             }
1223 
End(IAsyncResult result)1224             public static void End(IAsyncResult result)
1225             {
1226                 AsyncResult.End<CloseAsyncResult>(result);
1227             }
1228         }
1229     }
1230 }
1231