1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 
5 namespace System.ServiceModel
6 {
7     using System.Collections.Generic;
8     using System.Runtime;
9     using System.ServiceModel.Channels;
10     using System.Threading;
11 
InstanceContextEmptyCallback(InstanceContext instanceContext)12     delegate void InstanceContextEmptyCallback(InstanceContext instanceContext);
13 
14     class ServiceChannelManager : LifetimeManager
15     {
16         int activityCount;
17         ICommunicationWaiter activityWaiter;
18         int activityWaiterCount;
19         InstanceContextEmptyCallback emptyCallback;
20         IChannel firstIncomingChannel;
21         ChannelCollection incomingChannels;
22         ChannelCollection outgoingChannels;
23         InstanceContext instanceContext;
24 
ServiceChannelManager(InstanceContext instanceContext)25         public ServiceChannelManager(InstanceContext instanceContext)
26             : this(instanceContext, null)
27         {
28         }
29 
ServiceChannelManager(InstanceContext instanceContext, InstanceContextEmptyCallback emptyCallback)30         public ServiceChannelManager(InstanceContext instanceContext, InstanceContextEmptyCallback emptyCallback)
31             : base(instanceContext.ThisLock)
32         {
33             this.instanceContext = instanceContext;
34             this.emptyCallback = emptyCallback;
35         }
36 
37         public int ActivityCount
38         {
39             get { return this.activityCount; }
40         }
41 
42         public ICollection<IChannel> IncomingChannels
43         {
44             get
45             {
46                 this.EnsureIncomingChannelCollection();
47                 return (ICollection<IChannel>)this.incomingChannels;
48             }
49         }
50 
51         public ICollection<IChannel> OutgoingChannels
52         {
53             get
54             {
55                 if (this.outgoingChannels == null)
56                 {
57                     lock (this.ThisLock)
58                     {
59                         if (this.outgoingChannels == null)
60                             this.outgoingChannels = new ChannelCollection(this, this.ThisLock);
61                     }
62                 }
63                 return this.outgoingChannels;
64             }
65         }
66 
67         public bool IsBusy
68         {
69             get
70             {
71                 if (this.ActivityCount > 0)
72                     return true;
73 
74                 if (base.BusyCount > 0)
75                     return true;
76 
77                 ICollection<IChannel> outgoing = this.outgoingChannels;
78                 if ((outgoing != null) && (outgoing.Count > 0))
79                     return true;
80 
81                 return false;
82             }
83         }
84 
AddIncomingChannel(IChannel channel)85         public void AddIncomingChannel(IChannel channel)
86         {
87             bool added = false;
88 
89             lock (this.ThisLock)
90             {
91                 if (this.State == LifetimeState.Opened)
92                 {
93                     if (this.firstIncomingChannel == null)
94                     {
95                         if (this.incomingChannels == null)
96                         {
97                             this.firstIncomingChannel = channel;
98                             this.ChannelAdded(channel);
99                         }
100                         else
101                         {
102                             if (this.incomingChannels.Contains(channel))
103                                 return;
104                             this.incomingChannels.Add(channel);
105                         }
106                     }
107                     else
108                     {
109                         this.EnsureIncomingChannelCollection();
110                         if (this.incomingChannels.Contains(channel))
111                             return;
112                         this.incomingChannels.Add(channel);
113                     }
114                     added = true;
115                 }
116             }
117 
118             if (!added)
119             {
120                 channel.Abort();
121                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString()));
122             }
123         }
124 
BeginCloseInput(TimeSpan timeout, AsyncCallback callback, object state)125         public IAsyncResult BeginCloseInput(TimeSpan timeout, AsyncCallback callback, object state)
126         {
127             CloseCommunicationAsyncResult closeResult = null;
128 
129             lock (this.ThisLock)
130             {
131                 if (this.activityCount > 0)
132                 {
133                     closeResult = new CloseCommunicationAsyncResult(timeout, callback, state, this.ThisLock);
134 
135                     if (!(this.activityWaiter == null))
136                     {
137                         Fx.Assert("ServiceChannelManager.BeginCloseInput: (this.activityWaiter == null)");
138                     }
139                     this.activityWaiter = closeResult;
140                     Interlocked.Increment(ref this.activityWaiterCount);
141                 }
142             }
143 
144             if (closeResult != null)
145                 return closeResult;
146             else
147                 return new CompletedAsyncResult(callback, state);
148         }
149 
ChannelAdded(IChannel channel)150         void ChannelAdded(IChannel channel)
151         {
152             base.IncrementBusyCount();
153             channel.Closed += this.OnChannelClosed;
154         }
155 
ChannelRemoved(IChannel channel)156         void ChannelRemoved(IChannel channel)
157         {
158             channel.Closed -= this.OnChannelClosed;
159             base.DecrementBusyCount();
160         }
161 
162 
CloseInput(TimeSpan timeout)163         public void CloseInput(TimeSpan timeout)
164         {
165             SyncCommunicationWaiter activityWaiter = null;
166 
167             lock (this.ThisLock)
168             {
169                 if (this.activityCount > 0)
170                 {
171                     activityWaiter = new SyncCommunicationWaiter(this.ThisLock);
172                     if (!(this.activityWaiter == null))
173                     {
174                         Fx.Assert("ServiceChannelManager.CloseInput: (this.activityWaiter == null)");
175                     }
176                     this.activityWaiter = activityWaiter;
177                     Interlocked.Increment(ref this.activityWaiterCount);
178                 }
179             }
180 
181             if (activityWaiter != null)
182             {
183                 CommunicationWaitResult result = activityWaiter.Wait(timeout, false);
184                 if (Interlocked.Decrement(ref this.activityWaiterCount) == 0)
185                 {
186                     activityWaiter.Dispose();
187                     this.activityWaiter = null;
188                 }
189 
190                 switch (result)
191                 {
192                     case CommunicationWaitResult.Expired:
193                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(SR.SfxCloseTimedOutWaitingForDispatchToComplete)));
194                     case CommunicationWaitResult.Aborted:
195                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString()));
196                 }
197             }
198         }
199 
DecrementActivityCount()200         public void DecrementActivityCount()
201         {
202             ICommunicationWaiter activityWaiter = null;
203             bool empty = false;
204 
205             lock (this.ThisLock)
206             {
207                 if (!(this.activityCount > 0))
208                 {
209                     Fx.Assert("ServiceChannelManager.DecrementActivityCount: (this.activityCount > 0)");
210                 }
211                 if (--this.activityCount == 0)
212                 {
213                     if (this.activityWaiter != null)
214                     {
215                         activityWaiter = this.activityWaiter;
216                         Interlocked.Increment(ref this.activityWaiterCount);
217                     }
218                     if (this.BusyCount == 0)
219                         empty = true;
220                 }
221             }
222 
223             if (activityWaiter != null)
224             {
225                 activityWaiter.Signal();
226                 if (Interlocked.Decrement(ref this.activityWaiterCount) == 0)
227                 {
228                     activityWaiter.Dispose();
229                     this.activityWaiter = null;
230                 }
231             }
232 
233             if (empty && this.State == LifetimeState.Opened)
234                 OnEmpty();
235         }
236 
EndCloseInput(IAsyncResult result)237         public void EndCloseInput(IAsyncResult result)
238         {
239             if (result is CloseCommunicationAsyncResult)
240             {
241                 CloseCommunicationAsyncResult.End(result);
242                 if (Interlocked.Decrement(ref this.activityWaiterCount) == 0)
243                 {
244                     this.activityWaiter.Dispose();
245                     this.activityWaiter = null;
246                 }
247             }
248             else
249                 CompletedAsyncResult.End(result);
250         }
251 
EnsureIncomingChannelCollection()252         void EnsureIncomingChannelCollection()
253         {
254             lock (this.ThisLock)
255             {
256                 if (this.incomingChannels == null)
257                 {
258                     this.incomingChannels = new ChannelCollection(this, this.ThisLock);
259                     if (this.firstIncomingChannel != null)
260                     {
261                         this.incomingChannels.Add(this.firstIncomingChannel);
262                         this.ChannelRemoved(this.firstIncomingChannel); // Adding to collection called ChannelAdded, so call ChannelRemoved to balance
263                         this.firstIncomingChannel = null;
264                     }
265                 }
266             }
267         }
268 
IncrementActivityCount()269         public void IncrementActivityCount()
270         {
271             lock (this.ThisLock)
272             {
273                 if (this.State == LifetimeState.Closed)
274                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(this.GetType().ToString()));
275                 this.activityCount++;
276             }
277         }
278 
IncrementBusyCount()279         protected override void IncrementBusyCount()
280         {
281             base.IncrementBusyCount();
282         }
283 
OnAbort()284         protected override void OnAbort()
285         {
286             IChannel[] channels = this.SnapshotChannels();
287             for (int index = 0; index < channels.Length; index++)
288                 channels[index].Abort();
289 
290             ICommunicationWaiter activityWaiter = null;
291 
292             lock (this.ThisLock)
293             {
294                 if (this.activityWaiter != null)
295                 {
296                     activityWaiter = this.activityWaiter;
297                     Interlocked.Increment(ref this.activityWaiterCount);
298                 }
299             }
300 
301             if (activityWaiter != null)
302             {
303                 activityWaiter.Signal();
304                 if (Interlocked.Decrement(ref this.activityWaiterCount) == 0)
305                 {
306                     activityWaiter.Dispose();
307                     this.activityWaiter = null;
308                 }
309             }
310 
311             base.OnAbort();
312         }
313 
OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)314         protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
315         {
316             return new ChainedAsyncResult(timeout, callback, state, BeginCloseInput, EndCloseInput, OnBeginCloseContinue, OnEndCloseContinue);
317         }
318 
OnBeginCloseContinue(TimeSpan timeout, AsyncCallback callback, object state)319         IAsyncResult OnBeginCloseContinue(TimeSpan timeout, AsyncCallback callback, object state)
320         {
321             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
322             return base.OnBeginClose(timeoutHelper.RemainingTime(), callback, state);
323         }
324 
OnClose(TimeSpan timeout)325         protected override void OnClose(TimeSpan timeout)
326         {
327             TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
328 
329             this.CloseInput(timeoutHelper.RemainingTime());
330 
331             base.OnClose(timeoutHelper.RemainingTime());
332         }
333 
OnEndClose(IAsyncResult result)334         protected override void OnEndClose(IAsyncResult result)
335         {
336             ChainedAsyncResult.End(result);
337         }
338 
OnEndCloseContinue(IAsyncResult result)339         void OnEndCloseContinue(IAsyncResult result)
340         {
341             base.OnEndClose(result);
342         }
343 
OnEmpty()344         protected override void OnEmpty()
345         {
346             if (this.emptyCallback != null)
347                 this.emptyCallback(this.instanceContext);
348         }
349 
OnChannelClosed(object sender, EventArgs args)350         void OnChannelClosed(object sender, EventArgs args)
351         {
352             this.RemoveChannel((IChannel)sender);
353         }
354 
RemoveChannel(IChannel channel)355         public bool RemoveChannel(IChannel channel)
356         {
357             lock (this.ThisLock)
358             {
359                 if (this.firstIncomingChannel == channel)
360                 {
361                     this.firstIncomingChannel = null;
362                     this.ChannelRemoved(channel);
363                     return true;
364                 }
365                 else if (this.incomingChannels != null && this.incomingChannels.Contains(channel))
366                 {
367                     this.incomingChannels.Remove(channel);
368                     return true;
369                 }
370                 else if (this.outgoingChannels != null && this.outgoingChannels.Contains(channel))
371                 {
372                     this.outgoingChannels.Remove(channel);
373                     return true;
374                 }
375             }
376 
377             return false;
378         }
379 
SnapshotChannels()380         public IChannel[] SnapshotChannels()
381         {
382             lock (this.ThisLock)
383             {
384                 int outgoingCount = (this.outgoingChannels != null ? this.outgoingChannels.Count : 0);
385 
386                 if (this.firstIncomingChannel != null)
387                 {
388                     IChannel[] channels = new IChannel[1 + outgoingCount];
389                     channels[0] = this.firstIncomingChannel;
390                     if (outgoingCount > 0)
391                         this.outgoingChannels.CopyTo(channels, 1);
392                     return channels;
393                 }
394 
395                 if (this.incomingChannels != null)
396                 {
397                     IChannel[] channels = new IChannel[this.incomingChannels.Count + outgoingCount];
398                     this.incomingChannels.CopyTo(channels, 0);
399                     if (outgoingCount > 0)
400                         this.outgoingChannels.CopyTo(channels, this.incomingChannels.Count);
401                     return channels;
402                 }
403 
404                 if (outgoingCount > 0)
405                 {
406                     IChannel[] channels = new IChannel[outgoingCount];
407                     this.outgoingChannels.CopyTo(channels, 0);
408                     return channels;
409                 }
410             }
411             return EmptyArray<IChannel>.Allocate(0);
412         }
413 
414         class ChannelCollection : ICollection<IChannel>
415         {
416             ServiceChannelManager channelManager;
417             object syncRoot;
418             HashSet<IChannel> hashSet = new HashSet<IChannel>();
419 
420             public bool IsReadOnly
421             {
422                 get { return false; }
423             }
424 
425             public int Count
426             {
427                 get
428                 {
429                     lock (this.syncRoot)
430                     {
431                         return this.hashSet.Count;
432                     }
433                 }
434             }
435 
ChannelCollection(ServiceChannelManager channelManager, object syncRoot)436             public ChannelCollection(ServiceChannelManager channelManager, object syncRoot)
437             {
438                 if (syncRoot == null)
439                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("syncRoot"));
440 
441                 this.channelManager = channelManager;
442                 this.syncRoot = syncRoot;
443             }
444 
Add(IChannel channel)445             public void Add(IChannel channel)
446             {
447                 lock (this.syncRoot)
448                 {
449                     if (this.hashSet.Add(channel))
450                     {
451                         this.channelManager.ChannelAdded(channel);
452                     }
453                 }
454             }
455 
Clear()456             public void Clear()
457             {
458                 lock (this.syncRoot)
459                 {
460                     foreach (IChannel channel in this.hashSet)
461                         this.channelManager.ChannelRemoved(channel);
462                     this.hashSet.Clear();
463                 }
464             }
465 
Contains(IChannel channel)466             public bool Contains(IChannel channel)
467             {
468                 lock (this.syncRoot)
469                 {
470                     if (channel != null)
471                     {
472                         return this.hashSet.Contains(channel);
473                     }
474                     return false;
475                 }
476             }
477 
CopyTo(IChannel[] array, int arrayIndex)478             public void CopyTo(IChannel[] array, int arrayIndex)
479             {
480                 lock (this.syncRoot)
481                 {
482                     this.hashSet.CopyTo(array, arrayIndex);
483                 }
484             }
485 
Remove(IChannel channel)486             public bool Remove(IChannel channel)
487             {
488                 lock (this.syncRoot)
489                 {
490                     bool ret = false;
491                     if (channel != null)
492                     {
493                         ret = this.hashSet.Remove(channel);
494                         if (ret)
495                         {
496                             this.channelManager.ChannelRemoved(channel);
497                         }
498                     }
499                     return ret;
500                 }
501             }
502 
System.Collections.IEnumerable.GetEnumerator()503             System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
504             {
505                 lock (this.syncRoot)
506                 {
507                     return this.hashSet.GetEnumerator();
508                 }
509             }
510 
GetEnumerator()511             IEnumerator<IChannel> IEnumerable<IChannel>.GetEnumerator()
512             {
513                 lock (this.syncRoot)
514                 {
515                     return this.hashSet.GetEnumerator();
516                 }
517             }
518         }
519     }
520 }
521