1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 namespace System.ServiceModel.Channels 5 { 6 using System; 7 using System.Diagnostics; 8 using System.Runtime; 9 using System.ServiceModel; 10 using System.ServiceModel.Diagnostics; 11 using System.ServiceModel.Diagnostics.Application; 12 using System.Transactions; 13 using System.Runtime.Diagnostics; 14 15 public abstract class ReceiveContext 16 { 17 public readonly static string Name = "ReceiveContext"; 18 ThreadNeutralSemaphore stateLock; // protects state that may be reverted 19 bool contextFaulted; 20 object thisLock; 21 EventTraceActivity eventTraceActivity; 22 ReceiveContext()23 protected ReceiveContext() 24 { 25 this.thisLock = new object(); 26 this.State = ReceiveContextState.Received; 27 this.stateLock = new ThreadNeutralSemaphore(1); 28 } 29 30 public ReceiveContextState State 31 { 32 get; 33 protected set; 34 } 35 36 protected object ThisLock 37 { 38 get { return thisLock; } 39 } 40 41 public event EventHandler Faulted; 42 TryGet(Message message, out ReceiveContext property)43 public static bool TryGet(Message message, out ReceiveContext property) 44 { 45 if (message == null) 46 { 47 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message"); 48 } 49 50 bool result = TryGet(message.Properties, out property); 51 if (result && FxTrace.Trace.IsEnd2EndActivityTracingEnabled && property.eventTraceActivity == null) 52 { 53 property.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message); 54 } 55 56 return result; 57 } 58 TryGet(MessageProperties properties, out ReceiveContext property)59 public static bool TryGet(MessageProperties properties, out ReceiveContext property) 60 { 61 if (properties == null) 62 { 63 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("properties"); 64 } 65 66 property = null; 67 object foundProperty; 68 if (properties.TryGetValue(Name, out foundProperty)) 69 { 70 property = (ReceiveContext)foundProperty; 71 return true; 72 } 73 return false; 74 } 75 Abandon(TimeSpan timeout)76 public virtual void Abandon(TimeSpan timeout) 77 { 78 Abandon(null, timeout); 79 } 80 Abandon(Exception exception, TimeSpan timeout)81 public virtual void Abandon(Exception exception, TimeSpan timeout) 82 { 83 EnsureValidTimeout(timeout); 84 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 85 this.WaitForStateLock(timeoutHelper.RemainingTime()); 86 87 try 88 { 89 if (PreAbandon()) 90 { 91 return; 92 } 93 } 94 finally 95 { 96 // Abandon can never be reverted, release the state lock. 97 this.ReleaseStateLock(); 98 } 99 100 bool success = false; 101 try 102 { 103 if (exception == null) 104 { 105 OnAbandon(timeoutHelper.RemainingTime()); 106 } 107 else 108 { 109 if (TD.ReceiveContextAbandonWithExceptionIsEnabled()) 110 { 111 TD.ReceiveContextAbandonWithException(this.eventTraceActivity, this.GetType().ToString(), exception.GetType().ToString()); 112 } 113 OnAbandon(exception, timeoutHelper.RemainingTime()); 114 } 115 lock (ThisLock) 116 { 117 ThrowIfFaulted(); 118 ThrowIfNotAbandoning(); 119 this.State = ReceiveContextState.Abandoned; 120 } 121 success = true; 122 } 123 finally 124 { 125 if (!success) 126 { 127 if (TD.ReceiveContextAbandonFailedIsEnabled()) 128 { 129 TD.ReceiveContextAbandonFailed(this.eventTraceActivity, this.GetType().ToString()); 130 } 131 Fault(); 132 } 133 } 134 135 } 136 BeginAbandon(TimeSpan timeout, AsyncCallback callback, object state)137 public virtual IAsyncResult BeginAbandon(TimeSpan timeout, AsyncCallback callback, object state) 138 { 139 return BeginAbandon(null, timeout, callback, state); 140 } 141 BeginAbandon(Exception exception, TimeSpan timeout, AsyncCallback callback, object state)142 public virtual IAsyncResult BeginAbandon(Exception exception, TimeSpan timeout, AsyncCallback callback, object state) 143 { 144 EnsureValidTimeout(timeout); 145 return new AbandonAsyncResult(this, exception, timeout, callback, state); 146 } 147 BeginComplete(TimeSpan timeout, AsyncCallback callback, object state)148 public virtual IAsyncResult BeginComplete(TimeSpan timeout, AsyncCallback callback, object state) 149 { 150 EnsureValidTimeout(timeout); 151 return new CompleteAsyncResult(this, timeout, callback, state); 152 } 153 Complete(TimeSpan timeout)154 public virtual void Complete(TimeSpan timeout) 155 { 156 EnsureValidTimeout(timeout); 157 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); 158 this.WaitForStateLock(timeoutHelper.RemainingTime()); 159 bool success = false; 160 161 try 162 { 163 PreComplete(); 164 success = true; 165 } 166 finally 167 { 168 // Case 1: State validation fails, release the lock. 169 // Case 2: No trasaction, the state can never be reverted, release the lock. 170 // Case 3: Transaction, keep the lock until we know the transaction outcome (OnTransactionStatusNotification). 171 if (!success || Transaction.Current == null) 172 { 173 this.ReleaseStateLock(); 174 } 175 } 176 177 success = false; 178 try 179 { 180 OnComplete(timeoutHelper.RemainingTime()); 181 lock (ThisLock) 182 { 183 ThrowIfFaulted(); 184 ThrowIfNotCompleting(); 185 this.State = ReceiveContextState.Completed; 186 } 187 success = true; 188 } 189 finally 190 { 191 if (!success) 192 { 193 if (TD.ReceiveContextCompleteFailedIsEnabled()) 194 { 195 TD.ReceiveContextCompleteFailed(this.eventTraceActivity, this.GetType().ToString()); 196 } 197 Fault(); 198 } 199 } 200 } 201 EndAbandon(IAsyncResult result)202 public virtual void EndAbandon(IAsyncResult result) 203 { 204 AbandonAsyncResult.End(result); 205 } 206 EndComplete(IAsyncResult result)207 public virtual void EndComplete(IAsyncResult result) 208 { 209 CompleteAsyncResult.End(result); 210 } 211 EnsureValidTimeout(TimeSpan timeout)212 void EnsureValidTimeout(TimeSpan timeout) 213 { 214 if (timeout < TimeSpan.Zero) 215 { 216 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 217 new ArgumentOutOfRangeException("timeout", SR.GetString(SR.SFxTimeoutOutOfRange0))); 218 } 219 220 if (TimeoutHelper.IsTooLarge(timeout)) 221 { 222 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 223 new ArgumentOutOfRangeException("timeout", timeout, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig))); 224 } 225 } 226 Fault()227 protected internal virtual void Fault() 228 { 229 lock (ThisLock) 230 { 231 if (this.State == ReceiveContextState.Completed || this.State == ReceiveContextState.Abandoned || this.State == ReceiveContextState.Faulted) 232 { 233 return; 234 } 235 this.State = ReceiveContextState.Faulted; 236 } 237 OnFaulted(); 238 } 239 OnAbandon(TimeSpan timeout)240 protected abstract void OnAbandon(TimeSpan timeout); OnAbandon(Exception exception, TimeSpan timeout)241 protected virtual void OnAbandon(Exception exception, TimeSpan timeout) 242 { 243 // default implementation: delegate to non-exception overload, ignoring reason 244 OnAbandon(timeout); 245 } 246 OnBeginAbandon(TimeSpan timeout, AsyncCallback callback, object state)247 protected abstract IAsyncResult OnBeginAbandon(TimeSpan timeout, AsyncCallback callback, object state); OnBeginAbandon(Exception exception, TimeSpan timeout, AsyncCallback callback, object state)248 protected virtual IAsyncResult OnBeginAbandon(Exception exception, TimeSpan timeout, AsyncCallback callback, object state) 249 { 250 // default implementation: delegate to non-exception overload, ignoring reason 251 return OnBeginAbandon(timeout, callback, state); 252 } 253 OnBeginComplete(TimeSpan timeout, AsyncCallback callback, object state)254 protected abstract IAsyncResult OnBeginComplete(TimeSpan timeout, AsyncCallback callback, object state); 255 OnComplete(TimeSpan timeout)256 protected abstract void OnComplete(TimeSpan timeout); OnEndAbandon(IAsyncResult result)257 protected abstract void OnEndAbandon(IAsyncResult result); OnEndComplete(IAsyncResult result)258 protected abstract void OnEndComplete(IAsyncResult result); 259 OnFaulted()260 protected virtual void OnFaulted() 261 { 262 lock (ThisLock) 263 { 264 if (this.contextFaulted) 265 { 266 return; 267 } 268 this.contextFaulted = true; 269 } 270 271 if (TD.ReceiveContextFaultedIsEnabled()) 272 { 273 TD.ReceiveContextFaulted(this.eventTraceActivity, this); 274 } 275 276 EventHandler handler = this.Faulted; 277 278 if (handler != null) 279 { 280 try 281 { 282 handler(this, EventArgs.Empty); 283 } 284 catch (Exception exception) 285 { 286 if (Fx.IsFatal(exception)) 287 { 288 throw; 289 } 290 291 throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception); 292 } 293 } 294 } 295 OnTransactionStatusNotification(TransactionStatus status)296 void OnTransactionStatusNotification(TransactionStatus status) 297 { 298 lock (ThisLock) 299 { 300 if (status == TransactionStatus.Aborted) 301 { 302 if (this.State == ReceiveContextState.Completing || this.State == ReceiveContextState.Completed) 303 { 304 this.State = ReceiveContextState.Received; 305 } 306 } 307 } 308 309 if (status != TransactionStatus.Active) 310 { 311 this.ReleaseStateLock(); 312 } 313 } 314 PreAbandon()315 bool PreAbandon() 316 { 317 bool alreadyAbandoned = false; 318 lock (ThisLock) 319 { 320 if (this.State == ReceiveContextState.Abandoning || this.State == ReceiveContextState.Abandoned) 321 { 322 alreadyAbandoned = true; 323 } 324 else 325 { 326 ThrowIfFaulted(); 327 ThrowIfNotReceived(); 328 this.State = ReceiveContextState.Abandoning; 329 } 330 } 331 return alreadyAbandoned; 332 } 333 PreComplete()334 void PreComplete() 335 { 336 lock (ThisLock) 337 { 338 ThrowIfFaulted(); 339 ThrowIfNotReceived(); 340 if (Transaction.Current != null) 341 { 342 Transaction.Current.EnlistVolatile(new EnlistmentNotifications(this), EnlistmentOptions.None); 343 } 344 this.State = ReceiveContextState.Completing; 345 } 346 } 347 ReleaseStateLock()348 void ReleaseStateLock() 349 { 350 this.stateLock.Exit(); 351 } 352 ThrowIfFaulted()353 void ThrowIfFaulted() 354 { 355 356 if (State == ReceiveContextState.Faulted) 357 { 358 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 359 new CommunicationException(SR.GetString(SR.ReceiveContextFaulted, this.GetType().ToString()))); 360 } 361 } 362 ThrowIfNotAbandoning()363 void ThrowIfNotAbandoning() 364 { 365 if (State != ReceiveContextState.Abandoning) 366 { 367 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 368 new InvalidOperationException(SR.GetString(SR.ReceiveContextInInvalidState, this.GetType().ToString(), this.State.ToString()))); 369 } 370 } 371 ThrowIfNotCompleting()372 void ThrowIfNotCompleting() 373 { 374 if (State != ReceiveContextState.Completing) 375 { 376 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 377 new InvalidOperationException(SR.GetString(SR.ReceiveContextInInvalidState, this.GetType().ToString(), this.State.ToString()))); 378 } 379 } 380 ThrowIfNotReceived()381 void ThrowIfNotReceived() 382 { 383 if (State != ReceiveContextState.Received) 384 { 385 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( 386 new InvalidOperationException(SR.GetString(SR.ReceiveContextCannotBeUsed, this.GetType().ToString(), this.State.ToString()))); 387 } 388 } 389 WaitForStateLock(TimeSpan timeout)390 void WaitForStateLock(TimeSpan timeout) 391 { 392 try 393 { 394 this.stateLock.Enter(timeout); 395 } 396 catch (TimeoutException exception) 397 { 398 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(WrapStateException(exception)); 399 } 400 } 401 WaitForStateLockAsync(TimeSpan timeout, FastAsyncCallback callback, object state)402 bool WaitForStateLockAsync(TimeSpan timeout, FastAsyncCallback callback, object state) 403 { 404 return this.stateLock.EnterAsync(timeout, callback, state); 405 } 406 WrapStateException(Exception exception)407 Exception WrapStateException(Exception exception) 408 { 409 return new InvalidOperationException(SR.GetString(SR.ReceiveContextInInvalidState, this.GetType().ToString(), this.State.ToString()), exception); 410 } 411 412 sealed class AbandonAsyncResult : WaitAndContinueOperationAsyncResult 413 { 414 Exception exception; 415 static AsyncCompletion handleOperationComplete = new AsyncCompletion(HandleOperationComplete); 416 AbandonAsyncResult(ReceiveContext receiveContext, Exception exception, TimeSpan timeout, AsyncCallback callback, object state)417 public AbandonAsyncResult(ReceiveContext receiveContext, Exception exception, TimeSpan timeout, AsyncCallback callback, object state) 418 : base(receiveContext, timeout, callback, state) 419 { 420 this.exception = exception; 421 base.Begin(); 422 } 423 424 // The main Abandon logic. ContinueOperation()425 protected override bool ContinueOperation() 426 { 427 try 428 { 429 if (this.ReceiveContext.PreAbandon()) 430 { 431 return true; 432 } 433 } 434 finally 435 { 436 // Abandon can never be reverted, release the state lock. 437 this.ReceiveContext.ReleaseStateLock(); 438 } 439 440 bool success = false; 441 IAsyncResult result; 442 try 443 { 444 if (exception == null) 445 { 446 result = this.ReceiveContext.OnBeginAbandon(this.TimeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleOperationComplete), this); 447 } 448 else 449 { 450 if (TD.ReceiveContextAbandonWithExceptionIsEnabled()) 451 { 452 TD.ReceiveContextAbandonWithException(this.ReceiveContext.eventTraceActivity, this.GetType().ToString(), exception.GetType().ToString()); 453 } 454 455 result = this.ReceiveContext.OnBeginAbandon(exception, this.TimeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleOperationComplete), this); 456 } 457 458 success = true; 459 } 460 finally 461 { 462 if (!success) 463 { 464 if (TD.ReceiveContextAbandonFailedIsEnabled()) 465 { 466 TD.ReceiveContextAbandonFailed((this.ReceiveContext != null) ? this.ReceiveContext.eventTraceActivity : null, 467 this.GetType().ToString()); 468 } 469 470 this.ReceiveContext.Fault(); 471 } 472 } 473 474 return SyncContinue(result); 475 } 476 End(IAsyncResult result)477 public static void End(IAsyncResult result) 478 { 479 AsyncResult.End<AbandonAsyncResult>(result); 480 } 481 EndAbandon(IAsyncResult result)482 void EndAbandon(IAsyncResult result) 483 { 484 this.ReceiveContext.OnEndAbandon(result); 485 lock (this.ReceiveContext.ThisLock) 486 { 487 this.ReceiveContext.ThrowIfFaulted(); 488 this.ReceiveContext.ThrowIfNotAbandoning(); 489 this.ReceiveContext.State = ReceiveContextState.Abandoned; 490 } 491 } 492 HandleOperationComplete(IAsyncResult result)493 static bool HandleOperationComplete(IAsyncResult result) 494 { 495 bool success = false; 496 AbandonAsyncResult thisPtr = (AbandonAsyncResult)result.AsyncState; 497 498 try 499 { 500 thisPtr.EndAbandon(result); 501 success = true; 502 return true; 503 } 504 finally 505 { 506 if (!success) 507 { 508 if (TD.ReceiveContextAbandonFailedIsEnabled()) 509 { 510 TD.ReceiveContextAbandonFailed(thisPtr.ReceiveContext.eventTraceActivity, thisPtr.GetType().ToString()); 511 } 512 513 thisPtr.ReceiveContext.Fault(); 514 } 515 } 516 } 517 } 518 519 sealed class CompleteAsyncResult : WaitAndContinueOperationAsyncResult 520 { 521 Transaction transaction; 522 static AsyncCompletion handleOperationComplete = new AsyncCompletion(HandleOperationComplete); 523 CompleteAsyncResult(ReceiveContext receiveContext, TimeSpan timeout, AsyncCallback callback, object state)524 public CompleteAsyncResult(ReceiveContext receiveContext, TimeSpan timeout, AsyncCallback callback, object state) 525 : base(receiveContext, timeout, callback, state) 526 { 527 this.transaction = Transaction.Current; 528 this.Begin(); 529 } 530 ContinueOperation()531 protected override bool ContinueOperation() 532 { 533 IAsyncResult result; 534 535 using (PrepareTransactionalCall(this.transaction)) 536 { 537 bool success = false; 538 539 try 540 { 541 this.ReceiveContext.PreComplete(); 542 success = true; 543 } 544 finally 545 { 546 // Case 1: State validation fails, release the lock. 547 // Case 2: No trasaction, the state can never be reverted, release the lock. 548 // Case 3: Transaction, keep the lock until we know the transaction outcome (OnTransactionStatusNotification). 549 if (!success || this.transaction == null) 550 { 551 this.ReceiveContext.ReleaseStateLock(); 552 } 553 } 554 555 success = false; 556 557 try 558 { 559 result = this.ReceiveContext.OnBeginComplete(this.TimeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleOperationComplete), this); 560 success = true; 561 } 562 finally 563 { 564 if (!success) 565 { 566 if (TD.ReceiveContextCompleteFailedIsEnabled()) 567 { 568 TD.ReceiveContextCompleteFailed(this.ReceiveContext.eventTraceActivity, this.GetType().ToString()); 569 } 570 571 this.ReceiveContext.Fault(); 572 } 573 } 574 } 575 576 return SyncContinue(result); 577 } 578 End(IAsyncResult result)579 public static void End(IAsyncResult result) 580 { 581 AsyncResult.End<CompleteAsyncResult>(result); 582 } 583 EndComplete(IAsyncResult result)584 void EndComplete(IAsyncResult result) 585 { 586 this.ReceiveContext.OnEndComplete(result); 587 lock (this.ReceiveContext.ThisLock) 588 { 589 this.ReceiveContext.ThrowIfFaulted(); 590 this.ReceiveContext.ThrowIfNotCompleting(); 591 this.ReceiveContext.State = ReceiveContextState.Completed; 592 } 593 } 594 HandleOperationComplete(IAsyncResult result)595 static bool HandleOperationComplete(IAsyncResult result) 596 { 597 CompleteAsyncResult thisPtr = (CompleteAsyncResult)result.AsyncState; 598 bool success = false; 599 600 try 601 { 602 thisPtr.EndComplete(result); 603 success = true; 604 return true; 605 } 606 finally 607 { 608 if (!success) 609 { 610 if (TD.ReceiveContextCompleteFailedIsEnabled()) 611 { 612 TD.ReceiveContextCompleteFailed(thisPtr.ReceiveContext.eventTraceActivity, thisPtr.GetType().ToString()); 613 } 614 615 thisPtr.ReceiveContext.Fault(); 616 } 617 } 618 } 619 } 620 621 class EnlistmentNotifications : IEnlistmentNotification 622 { 623 ReceiveContext context; 624 EnlistmentNotifications(ReceiveContext context)625 public EnlistmentNotifications(ReceiveContext context) 626 { 627 this.context = context; 628 } 629 Commit(Enlistment enlistment)630 public void Commit(Enlistment enlistment) 631 { 632 this.context.OnTransactionStatusNotification(TransactionStatus.Committed); 633 enlistment.Done(); 634 } 635 InDoubt(Enlistment enlistment)636 public void InDoubt(Enlistment enlistment) 637 { 638 this.context.OnTransactionStatusNotification(TransactionStatus.InDoubt); 639 enlistment.Done(); 640 } 641 Prepare(PreparingEnlistment preparingEnlistment)642 public void Prepare(PreparingEnlistment preparingEnlistment) 643 { 644 this.context.OnTransactionStatusNotification(TransactionStatus.Active); 645 preparingEnlistment.Prepared(); 646 } 647 Rollback(Enlistment enlistment)648 public void Rollback(Enlistment enlistment) 649 { 650 this.context.OnTransactionStatusNotification(TransactionStatus.Aborted); 651 enlistment.Done(); 652 } 653 } 654 655 abstract class WaitAndContinueOperationAsyncResult : TransactedAsyncResult 656 { 657 static FastAsyncCallback onWaitForStateLockComplete = new FastAsyncCallback(OnWaitForStateLockComplete); 658 WaitAndContinueOperationAsyncResult(ReceiveContext receiveContext, TimeSpan timeout, AsyncCallback callback, object state)659 public WaitAndContinueOperationAsyncResult(ReceiveContext receiveContext, TimeSpan timeout, AsyncCallback callback, object state) 660 : base(callback, state) 661 { 662 this.ReceiveContext = receiveContext; 663 this.TimeoutHelper = new TimeoutHelper(timeout); 664 } 665 666 protected ReceiveContext ReceiveContext 667 { 668 get; 669 private set; 670 } 671 672 protected TimeoutHelper TimeoutHelper 673 { 674 get; 675 private set; 676 } 677 Begin()678 protected void Begin() 679 { 680 if (!this.ReceiveContext.WaitForStateLockAsync(this.TimeoutHelper.RemainingTime(), onWaitForStateLockComplete, this)) 681 { 682 return; 683 } 684 685 if (this.ContinueOperation()) 686 { 687 this.Complete(true); 688 } 689 } 690 ContinueOperation()691 protected abstract bool ContinueOperation(); 692 OnWaitForStateLockComplete(object state, Exception asyncException)693 static void OnWaitForStateLockComplete(object state, Exception asyncException) 694 { 695 WaitAndContinueOperationAsyncResult thisPtr = (WaitAndContinueOperationAsyncResult)state; 696 bool completeAsyncResult = true; 697 Exception completeException = null; 698 699 if (asyncException != null) 700 { 701 if (asyncException is TimeoutException) 702 { 703 asyncException = thisPtr.ReceiveContext.WrapStateException(asyncException); 704 } 705 706 completeException = asyncException; 707 } 708 else 709 { 710 try 711 { 712 completeAsyncResult = thisPtr.ContinueOperation(); 713 } 714 catch (Exception e) 715 { 716 if (Fx.IsFatal(e)) 717 { 718 throw; 719 } 720 721 completeException = e; 722 } 723 } 724 725 if (completeAsyncResult) 726 { 727 thisPtr.Complete(false, completeException); 728 } 729 } 730 } 731 } 732 } 733