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