1 //------------------------------------------------------------ 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //------------------------------------------------------------ 4 5 namespace System.ServiceModel.Activities 6 { 7 using System.Activities; 8 using System.Collections.Generic; 9 using System.Collections.ObjectModel; 10 using System.Diagnostics; 11 using System.Runtime; 12 using System.Runtime.Diagnostics; 13 using System.Security; 14 using System.Security.Permissions; 15 using System.ServiceModel.Activation; 16 using System.ServiceModel.Activities.Description; 17 using System.ServiceModel.Activities.Dispatcher; 18 using System.ServiceModel.Channels; 19 using System.ServiceModel.Description; 20 using System.ServiceModel.Diagnostics; 21 using System.ServiceModel.Dispatcher; 22 using System.Transactions; 23 using TD2 = System.ServiceModel.Diagnostics.Application.TD; 24 25 // This AsyncResult completion is very tricky. It can complete in two path. 26 // FastPath: When workflow executes synchronously(SendFault/SendReply called before ResumeBookmark completes) we complete the AsyncResult at HandleEndResumeBookmark; 27 // Async Path: If workflow goes async (SendFault/SendReply called after ResumeBookmark completes) we complete the AsyncResult at SendFault/SendReply. 28 class WorkflowOperationContext : AsyncResult 29 { 30 static readonly ReadOnlyDictionaryInternal<string, string> emptyDictionary = new ReadOnlyDictionaryInternal<string, string>(new Dictionary<string, string>()); 31 static readonly object[] emptyObjectArray = new object[0]; 32 static AsyncCompletion handleEndResumeBookmark = new AsyncCompletion(HandleEndResumeBookmark); 33 static AsyncCompletion handleEndWaitForPendingOperations = new AsyncCompletion(HandleEndWaitForPendingOperations); 34 static AsyncCompletion handleEndProcessReceiveContext; 35 static Action<AsyncResult, Exception> onCompleting = new Action<AsyncResult, Exception>(Finally); 36 object[] inputs; 37 string operationName; 38 object[] outputs; 39 object operationReturnValue; 40 object thisLock; 41 42 WorkflowServiceInstance workflowInstance; 43 Bookmark bookmark; 44 object bookmarkValue; 45 BookmarkScope bookmarkScope; 46 47 IInvokeReceivedNotification notification; 48 IAsyncResult pendingAsyncResult; 49 50 TimeoutHelper timeoutHelper; 51 Exception pendingException; 52 53 ReceiveContext receiveContext; 54 55 // perf counter data 56 bool performanceCountersEnabled; 57 long beginTime; 58 59 // tracing data 60 bool propagateActivity; 61 Guid ambientActivityId; 62 Guid e2eActivityId; 63 EventTraceActivity eventTraceActivity; 64 long beginOperation; 65 66 //Tracking for decrement of ASP.NET busy count 67 bool hasDecrementedBusyCount; 68 WorkflowOperationContext(object[] inputs, OperationContext operationContext, string operationName, bool performanceCountersEnabled, bool propagateActivity, Transaction currentTransaction, WorkflowServiceInstance workflowInstance, IInvokeReceivedNotification notification, WorkflowOperationBehavior behavior, ServiceEndpoint endpoint, TimeSpan timeout, AsyncCallback callback, object state)69 WorkflowOperationContext(object[] inputs, OperationContext operationContext, string operationName, 70 bool performanceCountersEnabled, bool propagateActivity, Transaction currentTransaction, 71 WorkflowServiceInstance workflowInstance, IInvokeReceivedNotification notification, WorkflowOperationBehavior behavior, ServiceEndpoint endpoint, 72 TimeSpan timeout, AsyncCallback callback, object state) 73 : base(callback, state) 74 { 75 this.inputs = inputs; 76 this.operationName = operationName; 77 this.OperationContext = operationContext; 78 this.ServiceEndpoint = endpoint; 79 this.CurrentTransaction = currentTransaction; 80 this.performanceCountersEnabled = performanceCountersEnabled; 81 this.propagateActivity = propagateActivity; 82 this.timeoutHelper = new TimeoutHelper(timeout); 83 this.workflowInstance = workflowInstance; 84 this.thisLock = new object(); 85 this.notification = notification; 86 this.OnCompleting = onCompleting; 87 88 // Resolve bookmark 89 Fx.Assert(behavior != null, "behavior must not be null!"); 90 this.bookmark = behavior.OnResolveBookmark(this, out this.bookmarkScope, out this.bookmarkValue); 91 Fx.Assert(this.bookmark != null, "bookmark must not be null!"); 92 93 bool completeSelf = false; 94 95 try 96 { 97 // set activity ID on the executing thread (Bug 113386) 98 if (TraceUtility.MessageFlowTracingOnly) 99 { 100 this.e2eActivityId = TraceUtility.GetReceivedActivityId(this.OperationContext); 101 DiagnosticTraceBase.ActivityId = this.e2eActivityId; 102 } 103 104 if (Fx.Trace.IsEtwProviderEnabled) 105 { 106 this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(this.OperationContext.IncomingMessage); 107 } 108 109 // Take ownership of the ReceiveContext when buffering is enabled by removing the property 110 if (this.workflowInstance.BufferedReceiveManager != null) 111 { 112 if (!ReceiveContext.TryGet(this.OperationContext.IncomingMessageProperties, out this.receiveContext)) 113 { 114 Fx.Assert("ReceiveContext expected when BufferedReceives are enabled"); 115 } 116 117 this.OperationContext.IncomingMessageProperties.Remove(ReceiveContext.Name); 118 } 119 120 completeSelf = ProcessRequest(); 121 } 122 catch (Exception e) 123 { 124 if (Fx.IsFatal(e)) 125 { 126 throw; 127 } 128 129 // Failing synchronously is one case where AsyncResult won't handle calling OnCompleting 130 OnCompleting(this, e); 131 throw; 132 } 133 134 if (completeSelf) 135 { 136 base.Complete(true); 137 } 138 } 139 140 public object[] Inputs 141 { 142 get 143 { 144 return this.inputs; 145 } 146 } 147 148 public OperationContext OperationContext 149 { 150 get; 151 private set; 152 } 153 154 public ServiceEndpoint ServiceEndpoint 155 { 156 get; 157 private set; 158 } 159 160 public Transaction CurrentTransaction 161 { 162 get; 163 private set; 164 } 165 166 public object BookmarkValue 167 { 168 get 169 { 170 return this.bookmarkValue; 171 } 172 } 173 174 public bool HasResponse 175 { 176 get 177 { 178 lock (this.thisLock) 179 { 180 return this.CurrentState == State.Completed || this.CurrentState == State.ResultReceived; 181 } 182 } 183 } 184 185 //Completion state of this AsyncResult guarded by propertyLock 186 State CurrentState 187 { 188 get; 189 set; 190 } 191 192 public Guid E2EActivityId 193 { 194 get 195 { 196 return this.e2eActivityId; 197 } 198 } 199 BeginProcessRequest(WorkflowServiceInstance workflowInstance, OperationContext operationContext, string operationName, object[] inputs, bool performanceCountersEnabled, bool propagateActivity, Transaction currentTransaction, IInvokeReceivedNotification notification, WorkflowOperationBehavior behavior, ServiceEndpoint endpoint, TimeSpan timeout, AsyncCallback callback, object state)200 public static IAsyncResult BeginProcessRequest(WorkflowServiceInstance workflowInstance, OperationContext operationContext, string operationName, 201 object[] inputs, bool performanceCountersEnabled, bool propagateActivity, Transaction currentTransaction, IInvokeReceivedNotification notification, 202 WorkflowOperationBehavior behavior, ServiceEndpoint endpoint, TimeSpan timeout, AsyncCallback callback, object state) 203 { 204 Fx.Assert(inputs != null, "Null inputs"); 205 return new WorkflowOperationContext(inputs, operationContext, operationName, performanceCountersEnabled, 206 propagateActivity, currentTransaction, workflowInstance, notification, behavior, endpoint, timeout, callback, state); 207 } 208 EndProcessRequest(IAsyncResult result, out object[] outputs)209 public static object EndProcessRequest(IAsyncResult result, out object[] outputs) 210 { 211 WorkflowOperationContext thisPtr = AsyncResult.End<WorkflowOperationContext>(result); 212 outputs = thisPtr.outputs; 213 return thisPtr.operationReturnValue; 214 } 215 SendFault(Exception exception)216 public void SendFault(Exception exception) 217 { 218 Fx.Assert(exception != null, "Null Exception"); 219 220 this.pendingException = exception; 221 222 bool completeNow; 223 224 lock (this.thisLock) 225 { 226 Fx.Assert(this.CurrentState != State.Completed && this.CurrentState != State.ResultReceived, "Cannot receive this call after completion/result"); 227 completeNow = ProcessReply(); 228 } 229 230 if (completeNow) 231 { 232 this.Complete(false, exception); 233 } 234 } 235 SendReply(Message returnValue)236 public void SendReply(Message returnValue) 237 { 238 bool completeNow; 239 240 lock (this.thisLock) 241 { 242 Fx.Assert(this.CurrentState != State.Completed && this.CurrentState != State.ResultReceived, "Cannot receive this call after completion/result"); 243 this.outputs = WorkflowOperationContext.emptyObjectArray; // everything is in the Message return value for workflow 244 this.operationReturnValue = returnValue; 245 completeNow = ProcessReply(); 246 } 247 248 if (completeNow) 249 { 250 base.Complete(false); 251 } 252 } 253 SendReply(object returnValue, object[] outputs)254 public void SendReply(object returnValue, object[] outputs) 255 { 256 bool completeNow; 257 258 lock (this.thisLock) 259 { 260 Fx.Assert(this.CurrentState != State.Completed && this.CurrentState != State.ResultReceived, "Cannot receive this call after completion/result"); 261 this.outputs = outputs ?? WorkflowOperationContext.emptyObjectArray; 262 this.operationReturnValue = returnValue; 263 completeNow = ProcessReply(); 264 } 265 266 if (completeNow) 267 { 268 base.Complete(false); 269 } 270 } 271 272 //No-op for two-ways. SetOperationCompleted()273 public void SetOperationCompleted() 274 { 275 bool completeNow; 276 277 lock (this.thisLock) 278 { 279 completeNow = ProcessReply(); 280 } 281 282 if (completeNow) 283 { 284 base.Complete(false); 285 } 286 } 287 ProcessReply()288 bool ProcessReply() 289 { 290 bool completed = false; 291 this.workflowInstance.ReleaseContext(this); 292 this.RemovePendingOperation(); 293 294 if (this.CurrentState == State.BookmarkResumption) //We are still in Bookmark Resume 295 { 296 this.CurrentState = State.ResultReceived; //HandleEndResumeBookmark will take care of Completing AsyncResult. 297 } 298 else if (this.CurrentState == State.WaitForResult) //We already finished the bookmarkOperation; Now have to signal the AsynResult. 299 { 300 this.CurrentState = State.Completed; 301 completed = true; 302 } 303 304 // we are not really completed until the ReceiveContext finishes its work 305 if (completed) 306 { 307 if (this.pendingException == null) 308 { 309 completed = ProcessReceiveContext(); 310 } 311 else 312 { 313 // if there's a pendingException, we let the RC abandon async so there's no need 314 // to affect the completed status 315 BufferedReceiveManager.AbandonReceiveContext(this.receiveContext); 316 } 317 } 318 319 return completed; 320 } 321 ProcessInitializationTraces()322 void ProcessInitializationTraces() 323 { 324 //Let asp.net know that it needs to wait 325 IncrementBusyCount(); 326 327 try 328 { 329 if (TraceUtility.MessageFlowTracingOnly) 330 { 331 //ensure that Activity ID is set 332 DiagnosticTraceBase.ActivityId = this.E2EActivityId; 333 this.propagateActivity = false; 334 } 335 if (TraceUtility.ActivityTracing || (!TraceUtility.MessageFlowTracing && this.propagateActivity)) 336 { 337 this.e2eActivityId = TraceUtility.GetReceivedActivityId(this.OperationContext); 338 339 if ((this.E2EActivityId != Guid.Empty) && (this.E2EActivityId != InternalReceiveMessage.TraceCorrelationActivityId)) 340 { 341 this.propagateActivity = true; 342 this.OperationContext.IncomingMessageProperties[MessagingActivityHelper.E2EActivityId] = this.E2EActivityId; 343 this.ambientActivityId = InternalReceiveMessage.TraceCorrelationActivityId; 344 FxTrace.Trace.SetAndTraceTransfer(this.E2EActivityId, true); 345 if (TD.StartSignpostEventIsEnabled()) 346 { 347 TD.StartSignpostEvent(new DictionaryTraceRecord(new Dictionary<string, string>(2) { 348 { MessagingActivityHelper.ActivityName, MessagingActivityHelper.ActivityNameWorkflowOperationInvoke }, 349 { MessagingActivityHelper.ActivityType, MessagingActivityHelper.ActivityTypeExecuteUserCode } 350 })); 351 } 352 } 353 else 354 { 355 this.propagateActivity = false; 356 } 357 } 358 } 359 catch (Exception ex) 360 { 361 if (Fx.IsFatal(ex)) 362 { 363 throw; 364 } 365 FxTrace.Exception.AsInformation(ex); 366 } 367 } 368 369 DecrementBusyCount()370 void DecrementBusyCount() 371 { 372 lock (this.thisLock) 373 { 374 if (!this.hasDecrementedBusyCount) 375 { 376 AspNetEnvironment.Current.DecrementBusyCount(); 377 if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled()) 378 { 379 AspNetEnvironment.Current.TraceDecrementBusyCount(SR.BusyCountTraceFormatString(this.workflowInstance.Id)); 380 } 381 this.hasDecrementedBusyCount = true; 382 } 383 } 384 } 385 IncrementBusyCount()386 void IncrementBusyCount() 387 { 388 AspNetEnvironment.Current.IncrementBusyCount(); 389 if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled()) 390 { 391 AspNetEnvironment.Current.TraceIncrementBusyCount(SR.BusyCountTraceFormatString(this.workflowInstance.Id)); 392 } 393 } 394 EmitTransferFromInstanceId()395 void EmitTransferFromInstanceId() 396 { 397 if (TraceUtility.MessageFlowTracing) 398 { 399 //set the WF instance ID as the Activity ID 400 if (DiagnosticTraceBase.ActivityId != this.workflowInstance.Id) 401 { 402 DiagnosticTraceBase.ActivityId = this.workflowInstance.Id; 403 } 404 FxTrace.Trace.SetAndTraceTransfer(this.E2EActivityId, true); 405 } 406 } 407 ProcessFinalizationTraces()408 void ProcessFinalizationTraces() 409 { 410 try 411 { 412 if (this.propagateActivity) 413 { 414 Guid oldId = InternalReceiveMessage.TraceCorrelationActivityId; 415 if (TD.StopSignpostEventIsEnabled()) 416 { 417 TD.StopSignpostEvent(new DictionaryTraceRecord(new Dictionary<string, string>(2) { 418 { MessagingActivityHelper.ActivityName, MessagingActivityHelper.ActivityNameWorkflowOperationInvoke }, 419 { MessagingActivityHelper.ActivityType, MessagingActivityHelper.ActivityTypeExecuteUserCode } 420 })); 421 } 422 FxTrace.Trace.SetAndTraceTransfer(this.ambientActivityId, true); 423 this.ambientActivityId = Guid.Empty; 424 } 425 } 426 catch (Exception ex) 427 { 428 if (Fx.IsFatal(ex)) 429 { 430 throw; 431 } 432 FxTrace.Exception.AsInformation(ex); 433 } 434 DecrementBusyCount(); 435 } 436 437 //Perf counter helpers. 438 [Fx.Tag.SecurityNote(Critical = "Critical because it accesses UnsafeNativeMethods.QueryPerformanceCounter.", 439 Safe = "Safe because we only make the call if the PartialTrustHelper.AppDomainFullyTrusted is true.")] 440 [SecuritySafeCritical] TrackMethodCalled()441 void TrackMethodCalled() 442 { 443 if (PartialTrustHelpers.AppDomainFullyTrusted && this.performanceCountersEnabled) 444 { 445 using (new OperationContextScopeHelper(this.OperationContext)) 446 { 447 PerformanceCounters.MethodCalled(this.operationName); 448 } 449 450 if (System.Runtime.Interop.UnsafeNativeMethods.QueryPerformanceCounter(out this.beginTime) == 0) 451 { 452 this.beginTime = -1; 453 } 454 } 455 456 if (TD2.OperationCompletedIsEnabled() || 457 TD2.OperationFaultedIsEnabled() || 458 TD2.OperationFailedIsEnabled()) 459 { 460 this.beginOperation = DateTime.UtcNow.Ticks; 461 } 462 463 if (TD2.OperationInvokedIsEnabled()) 464 { 465 using (new OperationContextScopeHelper(this.OperationContext)) 466 { 467 TD2.OperationInvoked(this.eventTraceActivity, this.operationName, TraceUtility.GetCallerInfo(this.OperationContext)); 468 } 469 } 470 } 471 TrackMethodFaulted()472 void TrackMethodFaulted() 473 { 474 if (this.performanceCountersEnabled) 475 { 476 long duration = this.GetDuration(); 477 using (new OperationContextScopeHelper(this.OperationContext)) 478 { 479 PerformanceCounters.MethodReturnedFault(this.operationName, duration); 480 } 481 } 482 if (TD2.OperationFaultedIsEnabled()) 483 { 484 using (new OperationContextScopeHelper(this.OperationContext)) 485 { 486 TD2.OperationFaulted(this.eventTraceActivity, this.operationName, 487 TraceUtility.GetUtcBasedDurationForTrace(this.beginOperation)); 488 } 489 } 490 } 491 TrackMethodFailed()492 void TrackMethodFailed() 493 { 494 if (this.performanceCountersEnabled) 495 { 496 long duration = this.GetDuration(); 497 using (new OperationContextScopeHelper(this.OperationContext)) 498 { 499 PerformanceCounters.MethodReturnedError(this.operationName, duration); 500 } 501 } 502 if (TD2.OperationFailedIsEnabled()) 503 { 504 using (new OperationContextScopeHelper(this.OperationContext)) 505 { 506 TD2.OperationFailed(this.eventTraceActivity, this.operationName, 507 TraceUtility.GetUtcBasedDurationForTrace(this.beginOperation)); 508 } 509 } 510 } 511 512 [Fx.Tag.SecurityNote(Critical = "Critical because it accesses UnsafeNativeMethods.QueryPerformanceCounter.", 513 Safe = "Safe because we only make the call if the PartialTrustHelper.AppDomainFullyTrusted is true.")] 514 [SecuritySafeCritical] GetDuration()515 long GetDuration() 516 { 517 long currentTime = 0; 518 long duration = 0; 519 520 if (PartialTrustHelpers.AppDomainFullyTrusted && this.performanceCountersEnabled && (this.beginTime >= 0) && 521 (System.Runtime.Interop.UnsafeNativeMethods.QueryPerformanceCounter(out currentTime) != 0)) 522 { 523 duration = currentTime - this.beginTime; 524 } 525 526 return duration; 527 } 528 TrackMethodSucceeded()529 void TrackMethodSucceeded() 530 { 531 if (this.performanceCountersEnabled) 532 { 533 long duration = this.GetDuration(); 534 using (new OperationContextScopeHelper(this.OperationContext)) 535 { 536 PerformanceCounters.MethodReturnedSuccess(this.operationName, duration); 537 } 538 } 539 if (TD2.OperationCompletedIsEnabled()) 540 { 541 using (new OperationContextScopeHelper(this.OperationContext)) 542 { 543 TD2.OperationCompleted(this.eventTraceActivity, this.operationName, 544 TraceUtility.GetUtcBasedDurationForTrace(this.beginOperation)); 545 } 546 } 547 } 548 TrackMethodCompleted(object returnValue)549 void TrackMethodCompleted(object returnValue) 550 { 551 // WorkflowOperationInvoker always deals at the Message/Message level 552 Message faultMessage = returnValue as Message; 553 if (faultMessage != null && faultMessage.IsFault) 554 { 555 TrackMethodFaulted(); 556 } 557 else 558 { 559 TrackMethodSucceeded(); 560 } 561 } 562 ProcessRequest()563 bool ProcessRequest() 564 { 565 this.TrackMethodCalled(); 566 this.ProcessInitializationTraces(); 567 568 if (this.notification == null) 569 { 570 return OnResumeBookmark(); 571 } 572 573 string sessionId = this.OperationContext.SessionId; 574 if (sessionId == null) 575 { 576 return OnResumeBookmark(); 577 } 578 579 // if there is a session, queue up this request in the per session pending request queue before notifying 580 // the dispatcher to start the next invoke 581 IAsyncResult pendingAsyncResult = this.workflowInstance.BeginWaitForPendingOperations(sessionId, this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(handleEndWaitForPendingOperations), this); 582 bool completed; 583 584 this.notification.NotifyInvokeReceived(); 585 if (pendingAsyncResult.CompletedSynchronously) 586 { 587 completed = HandleEndWaitForPendingOperations(pendingAsyncResult); 588 } 589 else 590 { 591 completed = false; 592 } 593 594 return completed; 595 } 596 RemovePendingOperation()597 void RemovePendingOperation() 598 { 599 if (this.pendingAsyncResult != null) 600 { 601 this.workflowInstance.RemovePendingOperation(this.OperationContext.SessionId, this.pendingAsyncResult); 602 this.pendingAsyncResult = null; 603 } 604 } 605 HandleEndWaitForPendingOperations(IAsyncResult result)606 static bool HandleEndWaitForPendingOperations(IAsyncResult result) 607 { 608 WorkflowOperationContext thisPtr = (WorkflowOperationContext)result.AsyncState; 609 thisPtr.pendingAsyncResult = result; 610 611 bool success = false; 612 try 613 { 614 thisPtr.workflowInstance.EndWaitForPendingOperations(result); 615 bool retval = thisPtr.OnResumeBookmark(); 616 success = true; 617 return retval; 618 } 619 finally 620 { 621 if (!success) 622 { 623 thisPtr.RemovePendingOperation(); 624 } 625 } 626 } 627 OnResumeBookmark()628 bool OnResumeBookmark() 629 { 630 bool success = false; 631 try 632 { 633 IAsyncResult nextResult = this.workflowInstance.BeginResumeProtocolBookmark( 634 this.bookmark, this.bookmarkScope, this, 635 this.timeoutHelper.RemainingTime(), this.PrepareAsyncCompletion(handleEndResumeBookmark), this); 636 637 bool completed; 638 if (nextResult.CompletedSynchronously) 639 { 640 completed = HandleEndResumeBookmark(nextResult); 641 } 642 else 643 { 644 completed = false; 645 } 646 647 success = true; 648 return completed; 649 } 650 finally 651 { 652 if (!success) 653 { 654 this.RemovePendingOperation(); 655 } 656 } 657 } 658 HandleEndResumeBookmark(IAsyncResult result)659 static bool HandleEndResumeBookmark(IAsyncResult result) 660 { 661 WorkflowOperationContext thisPtr = (WorkflowOperationContext)result.AsyncState; 662 663 bool completed = false; 664 bool shouldAbandon = true; 665 try 666 { 667 BookmarkResumptionResult resumptionResult = thisPtr.workflowInstance.EndResumeProtocolBookmark(result); 668 if (resumptionResult != BookmarkResumptionResult.Success) 669 { 670 // Raise UnkownMessageReceivedEvent when we fail to resume bookmark 671 thisPtr.OperationContext.Host.RaiseUnknownMessageReceived(thisPtr.OperationContext.IncomingMessage); 672 673 // Only delay-retry this operation once (and only if retries are supported). Future calls will ensure the bookmark is set. 674 if (thisPtr.workflowInstance.BufferedReceiveManager != null) 675 { 676 bool bufferSuccess = thisPtr.workflowInstance.BufferedReceiveManager.BufferReceive( 677 thisPtr.OperationContext, thisPtr.receiveContext, thisPtr.bookmark.Name, BufferedReceiveState.WaitingOnBookmark, false); 678 if (bufferSuccess) 679 { 680 if (TD.BufferOutOfOrderMessageNoBookmarkIsEnabled()) 681 { 682 TD.BufferOutOfOrderMessageNoBookmark(thisPtr.eventTraceActivity, thisPtr.workflowInstance.Id.ToString(), thisPtr.bookmark.Name); 683 } 684 685 shouldAbandon = false; 686 } 687 } 688 689 // The throw exception is intentional whether or not BufferedReceiveManager is set. 690 // This is to allow exception to bubble up the stack to WCF to cleanup various state (like Transaction). 691 // This is queue scenario and as far as the client is concerned, the client will not see any exception. 692 throw FxTrace.Exception.AsError(new FaultException(OperationExecutionFault.CreateOperationNotAvailableFault(thisPtr.workflowInstance.Id, thisPtr.bookmark.Name))); 693 } 694 695 lock (thisPtr.thisLock) 696 { 697 if (thisPtr.CurrentState == State.ResultReceived) 698 { 699 thisPtr.CurrentState = State.Completed; 700 if (thisPtr.pendingException != null) 701 { 702 throw FxTrace.Exception.AsError(thisPtr.pendingException); 703 } 704 completed = true; 705 } 706 else 707 { 708 thisPtr.CurrentState = State.WaitForResult; 709 completed = false; 710 } 711 712 // we are not really completed until the ReceiveContext finishes its work 713 if (completed) 714 { 715 completed = thisPtr.ProcessReceiveContext(); 716 } 717 718 shouldAbandon = false; 719 } 720 } 721 finally 722 { 723 if (shouldAbandon) 724 { 725 BufferedReceiveManager.AbandonReceiveContext(thisPtr.receiveContext); 726 } 727 thisPtr.RemovePendingOperation(); 728 } 729 730 return completed; 731 } 732 ProcessReceiveContext()733 bool ProcessReceiveContext() 734 { 735 if (this.receiveContext != null) 736 { 737 if (handleEndProcessReceiveContext == null) 738 { 739 handleEndProcessReceiveContext = new AsyncCompletion(HandleEndProcessReceiveContext); 740 } 741 742 IAsyncResult nextResult = ReceiveContextAsyncResult.BeginProcessReceiveContext(this, this.receiveContext, PrepareAsyncCompletion(handleEndProcessReceiveContext), this); 743 return SyncContinue(nextResult); 744 } 745 746 return true; 747 } 748 HandleEndProcessReceiveContext(IAsyncResult result)749 static bool HandleEndProcessReceiveContext(IAsyncResult result) 750 { 751 ReceiveContextAsyncResult.EndProcessReceiveContext(result); 752 return true; 753 } 754 Finally(AsyncResult result, Exception completionException)755 static void Finally(AsyncResult result, Exception completionException) 756 { 757 WorkflowOperationContext thisPtr = (WorkflowOperationContext)result; 758 thisPtr.EmitTransferFromInstanceId(); 759 760 if (completionException != null) 761 { 762 if (completionException is FaultException) 763 { 764 thisPtr.TrackMethodFaulted(); 765 } 766 else 767 { 768 thisPtr.TrackMethodFailed(); 769 } 770 } 771 else 772 { 773 thisPtr.TrackMethodCompleted(thisPtr.operationReturnValue); 774 } 775 thisPtr.ProcessFinalizationTraces(); 776 // will be a no-op if we were never added to the instance 777 thisPtr.workflowInstance.ReleaseContext(thisPtr); 778 thisPtr.RemovePendingOperation(); 779 } 780 781 enum State 782 { 783 BookmarkResumption, 784 WaitForResult, 785 ResultReceived, 786 Completed 787 } 788 789 class OperationContextScopeHelper : IDisposable 790 { 791 OperationContext currentOperationContext; 792 OperationContextScopeHelper(OperationContext operationContext)793 public OperationContextScopeHelper(OperationContext operationContext) 794 { 795 this.currentOperationContext = OperationContext.Current; 796 OperationContext.Current = operationContext; 797 } 798 IDisposable.Dispose()799 void IDisposable.Dispose() 800 { 801 OperationContext.Current = this.currentOperationContext; 802 } 803 } 804 805 class ReceiveContextAsyncResult : TransactedAsyncResult 806 { 807 static AsyncCompletion handleEndComplete = new AsyncCompletion(HandleEndComplete); 808 809 WorkflowOperationContext context; 810 ReceiveContext receiveContext; 811 ReceiveContextAsyncResult(WorkflowOperationContext context, ReceiveContext receiveContext, AsyncCallback callback, object state)812 ReceiveContextAsyncResult(WorkflowOperationContext context, ReceiveContext receiveContext, AsyncCallback callback, object state) 813 : base(callback, state) 814 { 815 this.context = context; 816 this.receiveContext = receiveContext; 817 818 if (ProcessReceiveContext()) 819 { 820 base.Complete(true); 821 } 822 } 823 BeginProcessReceiveContext(WorkflowOperationContext context, ReceiveContext receiveContext, AsyncCallback callback, object state)824 public static IAsyncResult BeginProcessReceiveContext(WorkflowOperationContext context, ReceiveContext receiveContext, AsyncCallback callback, object state) 825 { 826 return new ReceiveContextAsyncResult(context, receiveContext, callback, state); 827 } 828 EndProcessReceiveContext(IAsyncResult result)829 public static void EndProcessReceiveContext(IAsyncResult result) 830 { 831 ReceiveContextAsyncResult.End(result); 832 } 833 End(IAsyncResult result)834 public static void End(IAsyncResult result) 835 { 836 AsyncResult.End<ReceiveContextAsyncResult>(result); 837 } 838 ProcessReceiveContext()839 bool ProcessReceiveContext() 840 { 841 IAsyncResult result; 842 843 using (PrepareTransactionalCall(this.context.CurrentTransaction)) 844 { 845 if (this.context.CurrentTransaction != null) 846 { 847 // make sure we Abandon if the transaction ends up with an outcome of Aborted 848 this.context.CurrentTransaction.TransactionCompleted += new TransactionCompletedEventHandler(OnTransactionComplete); 849 } 850 result = this.receiveContext.BeginComplete( 851 this.context.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(handleEndComplete), this); 852 } 853 854 return SyncContinue(result); 855 } 856 857 // This happens out-of-band of ReceiveAsyncResult. 858 // When transaction was aborted, we best-effort abandon the context. OnTransactionComplete(object sender, TransactionEventArgs e)859 void OnTransactionComplete(object sender, TransactionEventArgs e) 860 { 861 if (e.Transaction.TransactionInformation.Status != TransactionStatus.Committed) 862 { 863 BufferedReceiveManager.AbandonReceiveContext(this.context.receiveContext); 864 } 865 } 866 HandleEndComplete(IAsyncResult result)867 static bool HandleEndComplete(IAsyncResult result) 868 { 869 ReceiveContextAsyncResult thisPtr = (ReceiveContextAsyncResult)result.AsyncState; 870 thisPtr.receiveContext.EndComplete(result); 871 return true; 872 } 873 } 874 } 875 } 876