Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Channels / TransportManager.cs
1 //----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------------------
4 namespace System.ServiceModel.Channels
5 {
6     using System.Collections.Generic;
7     using System.Diagnostics;
8     using System.Runtime;
9     using System.ServiceModel;
10     using System.ServiceModel.Diagnostics;
11     using System.Threading;
12     using System.ServiceModel.Diagnostics.Application;
13
14     abstract class TransportManager
15     {
16         ServiceModelActivity activity;
17         int openCount;
18         object thisLock = new object();
19
20         protected ServiceModelActivity Activity
21         {
22             get { return this.activity; }
23         }
24
25         internal abstract string Scheme { get; }
26
27         internal object ThisLock
28         {
29             get { return this.thisLock; }
30         }
31
32         internal void Close(TransportChannelListener channelListener, TimeSpan timeout)
33         {
34             this.Cleanup(channelListener, timeout, false);
35         }
36
37         void Cleanup(TransportChannelListener channelListener, TimeSpan timeout, bool aborting)
38         {
39             using (ServiceModelActivity.BoundOperation(this.Activity))
40             {
41                 this.Unregister(channelListener);
42             }
43             lock (ThisLock)
44             {
45                 if (openCount <= 0)
46                 {
47                     throw Fx.AssertAndThrow("Invalid Open/Close state machine.");
48                 }
49
50                 openCount--;
51
52                 if (openCount == 0)
53                 {
54                     // Wrap the final close here with transfers.
55                     using (ServiceModelActivity.BoundOperation(this.Activity, true))
56                     {
57                         if (aborting)
58                         {
59                             OnAbort();
60                         }
61                         else
62                         {
63                             OnClose(timeout);
64                         }
65                     }
66                     if (this.Activity != null)
67                     {
68                         this.Activity.Dispose();
69                     }
70                 }
71             }
72         }
73
74         internal static void EnsureRegistered<TChannelListener>(UriPrefixTable<TChannelListener> addressTable,
75             TChannelListener channelListener, HostNameComparisonMode registeredComparisonMode)
76             where TChannelListener : TransportChannelListener
77         {
78             TChannelListener existingFactory;
79             if (!addressTable.TryLookupUri(channelListener.Uri, registeredComparisonMode, out existingFactory) ||
80                 (existingFactory != channelListener))
81             {
82                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
83                     SR.ListenerFactoryNotRegistered, channelListener.Uri)));
84             }
85         }
86
87         // Must be called under lock(ThisLock).
88         protected void Fault<TChannelListener>(UriPrefixTable<TChannelListener> addressTable, Exception exception)
89             where TChannelListener : ChannelListenerBase
90         {
91             foreach (KeyValuePair<BaseUriWithWildcard, TChannelListener> pair in addressTable.GetAll())
92             {
93                 TChannelListener listener = pair.Value;
94                 listener.Fault(exception);
95                 listener.Abort();
96             }
97         }
98
99         internal abstract void OnClose(TimeSpan timeout);
100         internal abstract void OnOpen();
101         internal virtual void OnAbort() { }
102
103         internal void Open(TransportChannelListener channelListener)
104         {
105             if (DiagnosticUtility.ShouldUseActivity)
106             {
107                 if (this.activity == null)
108                 {
109                     this.activity = ServiceModelActivity.CreateActivity(true);
110                     if (DiagnosticUtility.ShouldUseActivity)
111                     {
112                         if (null != FxTrace.Trace)
113                         {
114                             FxTrace.Trace.TraceTransfer(this.Activity.Id);
115                         }
116                         ServiceModelActivity.Start(this.Activity, SR.GetString(SR.ActivityListenAt, channelListener.Uri.ToString()), ActivityType.ListenAt);
117                     }
118                 }
119                 channelListener.Activity = this.Activity;
120             }
121             using (ServiceModelActivity.BoundOperation(this.Activity))
122             {
123                 if (DiagnosticUtility.ShouldTraceInformation)
124                 {
125                     TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.TransportListen,
126                         SR.GetString(SR.TraceCodeTransportListen, channelListener.Uri.ToString()), this);
127                 }
128                 this.Register(channelListener);
129                 try
130                 {
131                     lock (ThisLock)
132                     {
133                         if (openCount == 0)
134                         {
135                             OnOpen();
136                         }
137
138                         openCount++;
139                     }
140                 }
141                 catch
142                 {
143                     this.Unregister(channelListener);
144                     throw;
145                 }
146             }
147         }
148
149         internal void Abort(TransportChannelListener channelListener)
150         {
151             this.Cleanup(channelListener, TimeSpan.Zero, true);
152         }
153
154         internal abstract void Register(TransportChannelListener channelListener);
155
156         // should only call this under ThisLock (unless accessing purely for inspection)
157         protected void ThrowIfOpen()
158         {
159             if (openCount > 0)
160             {
161                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
162                     new InvalidOperationException(SR.GetString(SR.TransportManagerOpen)));
163             }
164         }
165
166         internal abstract void Unregister(TransportChannelListener channelListener);
167     }
168
169
170     delegate IList<TransportManager> SelectTransportManagersCallback();
171     class TransportManagerContainer
172     {
173         IList<TransportManager> transportManagers;
174         TransportChannelListener listener;
175         bool closed;
176         object tableLock;
177
178         public TransportManagerContainer(TransportChannelListener listener)
179         {
180             this.listener = listener;
181             this.tableLock = listener.TransportManagerTable;
182             this.transportManagers = new List<TransportManager>();
183         }
184
185         TransportManagerContainer(TransportManagerContainer source)
186         {
187             this.listener = source.listener;
188             this.tableLock = source.tableLock;
189             this.transportManagers = new List<TransportManager>();
190             for (int i = 0; i < source.transportManagers.Count; i++)
191             {
192                 this.transportManagers.Add(source.transportManagers[i]);
193             }
194         }
195
196         // copy contents into a new container (used for listener/channel lifetime decoupling)
197         public static TransportManagerContainer TransferTransportManagers(TransportManagerContainer source)
198         {
199             TransportManagerContainer result = null;
200
201             lock (source.tableLock)
202             {
203                 if (source.transportManagers.Count > 0)
204                 {
205                     result = new TransportManagerContainer(source);
206                     source.transportManagers.Clear();
207                 }
208             }
209
210             return result;
211         }
212
213         public void Abort()
214         {
215             Close(true, TimeSpan.Zero);
216         }
217
218         public IAsyncResult BeginOpen(SelectTransportManagersCallback selectTransportManagerCallback,
219             AsyncCallback callback, object state)
220         {
221             return new OpenAsyncResult(selectTransportManagerCallback, this, callback, state);
222         }
223
224         public void EndOpen(IAsyncResult result)
225         {
226             OpenAsyncResult.End(result);
227         }
228
229         public void Open(SelectTransportManagersCallback selectTransportManagerCallback)
230         {
231             lock (this.tableLock)
232             {
233                 if (closed) // if we've been aborted then don't get transport managers
234                 {
235                     return;
236                 }
237
238                 IList<TransportManager> foundTransportManagers = selectTransportManagerCallback();
239                 if (foundTransportManagers == null) // nothing to do
240                 {
241                     return;
242                 }
243
244                 for (int i = 0; i < foundTransportManagers.Count; i++)
245                 {
246                     TransportManager transportManager = foundTransportManagers[i];
247                     transportManager.Open(this.listener);
248                     this.transportManagers.Add(transportManager);
249                 }
250             }
251         }
252
253         public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
254         {
255             return new CloseAsyncResult(this, callback, timeout, state);
256         }
257
258         public void EndClose(IAsyncResult result)
259         {
260             CloseAsyncResult.End(result);
261         }
262
263         public void Close(TimeSpan timeout)
264         {
265             Close(false, timeout);
266         }
267
268         public void Close(bool aborting, TimeSpan timeout)
269         {
270             if (this.closed)
271             {
272                 return;
273             }
274
275             IList<TransportManager> transportManagersCopy;
276             lock (this.tableLock)
277             {
278                 if (this.closed)
279                 {
280                     return;
281                 }
282
283                 this.closed = true;
284
285                 transportManagersCopy = new List<TransportManager>(this.transportManagers);
286                 this.transportManagers.Clear();
287
288                 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
289                 TimeoutException timeoutException = null;
290                 foreach (TransportManager transportManager in transportManagersCopy)
291                 {
292                     try
293                     {
294                         if (!aborting && timeoutException == null)
295                         {
296                             transportManager.Close(listener, timeoutHelper.RemainingTime());
297                         }
298                         else
299                         {
300                             transportManager.Abort(listener);
301                         }
302                     }
303                     catch (TimeoutException ex)
304                     {
305                         timeoutException = ex;
306                         transportManager.Abort(listener);
307                     }
308                 }
309
310                 if (timeoutException != null)
311                 {
312                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.TimeoutOnClose, timeout), timeoutException));
313                 }
314             }
315         }
316
317         abstract class OpenOrCloseAsyncResult : TraceAsyncResult
318         {
319             TransportManagerContainer parent;
320             static Action<object> scheduledCallback = new Action<object>(OnScheduled);
321
322             protected OpenOrCloseAsyncResult(TransportManagerContainer parent, AsyncCallback callback,
323                 object state)
324                 : base(callback, state)
325             {
326                 this.parent = parent;
327             }
328
329             protected void Begin()
330             {
331                 ActionItem.Schedule(scheduledCallback, this);
332             }
333
334             static void OnScheduled(object state)
335             {
336                 ((OpenOrCloseAsyncResult)state).OnScheduled();
337             }
338
339             void OnScheduled()
340             {
341                 using (ServiceModelActivity.BoundOperation(this.CallbackActivity))
342                 {
343                     Exception completionException = null;
344                     try
345                     {
346                         this.OnScheduled(this.parent);
347                     }
348 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
349                     catch (Exception e)
350                     {
351                         if (Fx.IsFatal(e))
352                         {
353                             throw;
354                         }
355                         completionException = e;
356                     }
357
358                     this.Complete(false, completionException);
359                 }
360             }
361
362             protected abstract void OnScheduled(TransportManagerContainer parent);
363         }
364
365         sealed class CloseAsyncResult : OpenOrCloseAsyncResult
366         {
367             TimeoutHelper timeoutHelper;
368
369             public CloseAsyncResult(TransportManagerContainer parent, AsyncCallback callback, TimeSpan timeout,
370                 object state)
371                 : base(parent, callback, state)
372             {
373                 this.timeoutHelper = new TimeoutHelper(timeout);
374                 this.timeoutHelper.RemainingTime(); //start count down
375                 this.Begin();
376             }
377
378             public static void End(IAsyncResult result)
379             {
380                 AsyncResult.End<CloseAsyncResult>(result);
381             }
382
383             protected override void OnScheduled(TransportManagerContainer parent)
384             {
385                 parent.Close(timeoutHelper.RemainingTime());
386             }
387         }
388
389         sealed class OpenAsyncResult : OpenOrCloseAsyncResult
390         {
391             SelectTransportManagersCallback selectTransportManagerCallback;
392
393             public OpenAsyncResult(SelectTransportManagersCallback selectTransportManagerCallback, TransportManagerContainer parent,
394                 AsyncCallback callback, object state)
395                 : base(parent, callback, state)
396             {
397                 this.selectTransportManagerCallback = selectTransportManagerCallback;
398                 this.Begin();
399             }
400
401             public static void End(IAsyncResult result)
402             {
403                 AsyncResult.End<OpenAsyncResult>(result);
404             }
405
406             protected override void OnScheduled(TransportManagerContainer parent)
407             {
408                 parent.Open(this.selectTransportManagerCallback);
409             }
410         }
411     }
412 }