1 //----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------------------
4 namespace System.ServiceModel.Channels
6 using System.Collections.Generic;
7 using System.Diagnostics;
9 using System.ServiceModel;
10 using System.ServiceModel.Diagnostics;
11 using System.Threading;
12 using System.ServiceModel.Diagnostics.Application;
14 abstract class TransportManager
16 ServiceModelActivity activity;
18 object thisLock = new object();
20 protected ServiceModelActivity Activity
22 get { return this.activity; }
25 internal abstract string Scheme { get; }
27 internal object ThisLock
29 get { return this.thisLock; }
32 internal void Close(TransportChannelListener channelListener, TimeSpan timeout)
34 this.Cleanup(channelListener, timeout, false);
37 void Cleanup(TransportChannelListener channelListener, TimeSpan timeout, bool aborting)
39 using (ServiceModelActivity.BoundOperation(this.Activity))
41 this.Unregister(channelListener);
47 throw Fx.AssertAndThrow("Invalid Open/Close state machine.");
54 // Wrap the final close here with transfers.
55 using (ServiceModelActivity.BoundOperation(this.Activity, true))
66 if (this.Activity != null)
68 this.Activity.Dispose();
74 internal static void EnsureRegistered<TChannelListener>(UriPrefixTable<TChannelListener> addressTable,
75 TChannelListener channelListener, HostNameComparisonMode registeredComparisonMode)
76 where TChannelListener : TransportChannelListener
78 TChannelListener existingFactory;
79 if (!addressTable.TryLookupUri(channelListener.Uri, registeredComparisonMode, out existingFactory) ||
80 (existingFactory != channelListener))
82 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
83 SR.ListenerFactoryNotRegistered, channelListener.Uri)));
87 // Must be called under lock(ThisLock).
88 protected void Fault<TChannelListener>(UriPrefixTable<TChannelListener> addressTable, Exception exception)
89 where TChannelListener : ChannelListenerBase
91 foreach (KeyValuePair<BaseUriWithWildcard, TChannelListener> pair in addressTable.GetAll())
93 TChannelListener listener = pair.Value;
94 listener.Fault(exception);
99 internal abstract void OnClose(TimeSpan timeout);
100 internal abstract void OnOpen();
101 internal virtual void OnAbort() { }
103 internal void Open(TransportChannelListener channelListener)
105 if (DiagnosticUtility.ShouldUseActivity)
107 if (this.activity == null)
109 this.activity = ServiceModelActivity.CreateActivity(true);
110 if (DiagnosticUtility.ShouldUseActivity)
112 if (null != FxTrace.Trace)
114 FxTrace.Trace.TraceTransfer(this.Activity.Id);
116 ServiceModelActivity.Start(this.Activity, SR.GetString(SR.ActivityListenAt, channelListener.Uri.ToString()), ActivityType.ListenAt);
119 channelListener.Activity = this.Activity;
121 using (ServiceModelActivity.BoundOperation(this.Activity))
123 if (DiagnosticUtility.ShouldTraceInformation)
125 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.TransportListen,
126 SR.GetString(SR.TraceCodeTransportListen, channelListener.Uri.ToString()), this);
128 this.Register(channelListener);
143 this.Unregister(channelListener);
149 internal void Abort(TransportChannelListener channelListener)
151 this.Cleanup(channelListener, TimeSpan.Zero, true);
154 internal abstract void Register(TransportChannelListener channelListener);
156 // should only call this under ThisLock (unless accessing purely for inspection)
157 protected void ThrowIfOpen()
161 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
162 new InvalidOperationException(SR.GetString(SR.TransportManagerOpen)));
166 internal abstract void Unregister(TransportChannelListener channelListener);
170 delegate IList<TransportManager> SelectTransportManagersCallback();
171 class TransportManagerContainer
173 IList<TransportManager> transportManagers;
174 TransportChannelListener listener;
178 public TransportManagerContainer(TransportChannelListener listener)
180 this.listener = listener;
181 this.tableLock = listener.TransportManagerTable;
182 this.transportManagers = new List<TransportManager>();
185 TransportManagerContainer(TransportManagerContainer source)
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++)
192 this.transportManagers.Add(source.transportManagers[i]);
196 // copy contents into a new container (used for listener/channel lifetime decoupling)
197 public static TransportManagerContainer TransferTransportManagers(TransportManagerContainer source)
199 TransportManagerContainer result = null;
201 lock (source.tableLock)
203 if (source.transportManagers.Count > 0)
205 result = new TransportManagerContainer(source);
206 source.transportManagers.Clear();
215 Close(true, TimeSpan.Zero);
218 public IAsyncResult BeginOpen(SelectTransportManagersCallback selectTransportManagerCallback,
219 AsyncCallback callback, object state)
221 return new OpenAsyncResult(selectTransportManagerCallback, this, callback, state);
224 public void EndOpen(IAsyncResult result)
226 OpenAsyncResult.End(result);
229 public void Open(SelectTransportManagersCallback selectTransportManagerCallback)
231 lock (this.tableLock)
233 if (closed) // if we've been aborted then don't get transport managers
238 IList<TransportManager> foundTransportManagers = selectTransportManagerCallback();
239 if (foundTransportManagers == null) // nothing to do
244 for (int i = 0; i < foundTransportManagers.Count; i++)
246 TransportManager transportManager = foundTransportManagers[i];
247 transportManager.Open(this.listener);
248 this.transportManagers.Add(transportManager);
253 public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
255 return new CloseAsyncResult(this, callback, timeout, state);
258 public void EndClose(IAsyncResult result)
260 CloseAsyncResult.End(result);
263 public void Close(TimeSpan timeout)
265 Close(false, timeout);
268 public void Close(bool aborting, TimeSpan timeout)
275 IList<TransportManager> transportManagersCopy;
276 lock (this.tableLock)
285 transportManagersCopy = new List<TransportManager>(this.transportManagers);
286 this.transportManagers.Clear();
288 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
289 TimeoutException timeoutException = null;
290 foreach (TransportManager transportManager in transportManagersCopy)
294 if (!aborting && timeoutException == null)
296 transportManager.Close(listener, timeoutHelper.RemainingTime());
300 transportManager.Abort(listener);
303 catch (TimeoutException ex)
305 timeoutException = ex;
306 transportManager.Abort(listener);
310 if (timeoutException != null)
312 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.TimeoutOnClose, timeout), timeoutException));
317 abstract class OpenOrCloseAsyncResult : TraceAsyncResult
319 TransportManagerContainer parent;
320 static Action<object> scheduledCallback = new Action<object>(OnScheduled);
322 protected OpenOrCloseAsyncResult(TransportManagerContainer parent, AsyncCallback callback,
324 : base(callback, state)
326 this.parent = parent;
329 protected void Begin()
331 ActionItem.Schedule(scheduledCallback, this);
334 static void OnScheduled(object state)
336 ((OpenOrCloseAsyncResult)state).OnScheduled();
341 using (ServiceModelActivity.BoundOperation(this.CallbackActivity))
343 Exception completionException = null;
346 this.OnScheduled(this.parent);
348 #pragma warning suppress 56500 // Microsoft, transferring exception to another thread
355 completionException = e;
358 this.Complete(false, completionException);
362 protected abstract void OnScheduled(TransportManagerContainer parent);
365 sealed class CloseAsyncResult : OpenOrCloseAsyncResult
367 TimeoutHelper timeoutHelper;
369 public CloseAsyncResult(TransportManagerContainer parent, AsyncCallback callback, TimeSpan timeout,
371 : base(parent, callback, state)
373 this.timeoutHelper = new TimeoutHelper(timeout);
374 this.timeoutHelper.RemainingTime(); //start count down
378 public static void End(IAsyncResult result)
380 AsyncResult.End<CloseAsyncResult>(result);
383 protected override void OnScheduled(TransportManagerContainer parent)
385 parent.Close(timeoutHelper.RemainingTime());
389 sealed class OpenAsyncResult : OpenOrCloseAsyncResult
391 SelectTransportManagersCallback selectTransportManagerCallback;
393 public OpenAsyncResult(SelectTransportManagersCallback selectTransportManagerCallback, TransportManagerContainer parent,
394 AsyncCallback callback, object state)
395 : base(parent, callback, state)
397 this.selectTransportManagerCallback = selectTransportManagerCallback;
401 public static void End(IAsyncResult result)
403 AsyncResult.End<OpenAsyncResult>(result);
406 protected override void OnScheduled(TransportManagerContainer parent)
408 parent.Open(this.selectTransportManagerCallback);