1 #pragma warning disable 1634, 1691 2 using System; 3 using System.Globalization; 4 using System.Collections; 5 using System.Collections.Generic; 6 using System.Collections.Specialized; 7 using System.Collections.ObjectModel; 8 using System.ComponentModel.Design.Serialization; 9 using System.Diagnostics; 10 using System.IO; 11 using System.Reflection; 12 using System.Text; 13 using System.Threading; 14 using System.Xml; 15 using System.Transactions; 16 using SES = System.EnterpriseServices; 17 using System.Workflow.ComponentModel; 18 using System.Workflow.Runtime.Hosting; 19 using System.Workflow.Runtime.DebugEngine; 20 21 namespace System.Workflow.Runtime 22 { 23 /// <remarks> 24 /// The runtime object that represents the schedule. 25 /// </remarks> 26 internal sealed class WorkflowExecutor : IWorkflowCoreRuntime, IServiceProvider, ISupportInterop 27 { 28 internal readonly static DependencyProperty WorkflowExecutorProperty = DependencyProperty.RegisterAttached("WorkflowExecutor", typeof(IWorkflowCoreRuntime), typeof(WorkflowExecutor), new PropertyMetadata(DependencyPropertyOptions.NonSerialized)); 29 // The static method GetTransientBatch is used by this property to retrieve the WorkBatch. 30 // GetTransientBatch is defined in this class but if the workflow is running under a V2.0 Interop environment, 31 // it forwards the call to the Interop activity. 32 internal readonly static DependencyProperty TransientBatchProperty = DependencyProperty.RegisterAttached("TransientBatch", typeof(IWorkBatch), typeof(WorkflowExecutor), new PropertyMetadata(null, DependencyPropertyOptions.NonSerialized, new GetValueOverride(GetTransientBatch), null)); 33 internal readonly static DependencyProperty TransactionalPropertiesProperty = DependencyProperty.RegisterAttached("TransactionalProperties", typeof(TransactionalProperties), typeof(WorkflowExecutor), new PropertyMetadata(DependencyPropertyOptions.NonSerialized)); 34 internal readonly static DependencyProperty WorkflowInstanceIdProperty = DependencyProperty.RegisterAttached("WorkflowInstanceId", typeof(Guid), typeof(WorkflowExecutor), new PropertyMetadata(Guid.NewGuid())); 35 internal readonly static DependencyProperty IsBlockedProperty = DependencyProperty.RegisterAttached("IsBlocked", typeof(bool), typeof(WorkflowExecutor), new PropertyMetadata(false)); 36 internal readonly static DependencyProperty WorkflowStatusProperty = DependencyProperty.RegisterAttached("WorkflowStatus", typeof(WorkflowStatus), typeof(WorkflowExecutor), new PropertyMetadata(WorkflowStatus.Created)); 37 internal readonly static DependencyProperty SuspendOrTerminateInfoProperty = DependencyProperty.RegisterAttached("SuspendOrTerminateInfo", typeof(string), typeof(WorkflowExecutor)); 38 39 // Persisted state properties 40 private static DependencyProperty ContextIdProperty = DependencyProperty.RegisterAttached("ContextId", typeof(int), typeof(WorkflowExecutor), new PropertyMetadata(new Int32())); 41 private static DependencyProperty TrackingCallingStateProperty = DependencyProperty.RegisterAttached("TrackingCallingState", typeof(TrackingCallingState), typeof(WorkflowExecutor)); 42 internal static DependencyProperty TrackingListenerBrokerProperty = DependencyProperty.RegisterAttached("TrackingListenerBroker", typeof(TrackingListenerBroker), typeof(WorkflowExecutor)); 43 private static DependencyProperty IsSuspensionRequestedProperty = DependencyProperty.RegisterAttached("IsSuspensionRequested", typeof(bool), typeof(WorkflowExecutor), new PropertyMetadata(false)); 44 private static DependencyProperty IsIdleProperty = DependencyProperty.RegisterAttached("IsIdle", typeof(bool), typeof(WorkflowExecutor), new PropertyMetadata(false)); 45 46 #region Data Members - Please keep all the data here 47 48 internal Activity currentAtomicActivity; 49 private ManualResetEvent atomicActivityEvent; 50 51 private Hashtable completedContextActivities = new Hashtable(); 52 53 Activity rootActivity; 54 55 WorkflowRuntime _runtime; // hosting environment 56 57 private VolatileResourceManager _resourceManager = new VolatileResourceManager(); 58 bool _isInstanceValid; 59 private bool isInstanceIdle; 60 Activity _lastExecutingActivity; 61 62 private Scheduler schedulingContext; 63 private WorkflowQueuingService qService; 64 65 private Exception thrownException; 66 private string activityThrowingException; 67 68 private List<SchedulerLockGuardInfo> eventsToFireList = new List<SchedulerLockGuardInfo>(); 69 70 internal bool stateChangedSincePersistence; 71 72 private WorkflowInstance _workflowInstance; 73 private Guid workflowInstanceId; 74 private string workflowIdString = null; 75 76 WorkflowStateRollbackService workflowStateRollbackService; 77 78 private InstanceLock _executorLock; 79 private InstanceLock _msgDeliveryLock; 80 private InstanceLock _schedulerLock; 81 private TimerEventSubscriptionCollection _timerQueue; 82 private volatile Activity _workflowDefinition; // dependency property cache 83 84 private static BooleanSwitch disableWorkflowDebugging = new BooleanSwitch("DisableWorkflowDebugging", "Disables workflow debugging in host"); 85 private static bool workflowDebuggingDisabled; 86 private WorkflowDebuggerService _workflowDebuggerService; 87 #endregion Data Members 88 89 #region Ctors 90 WorkflowExecutor()91 static WorkflowExecutor() 92 { 93 // registered by workflow executor 94 DependencyProperty.RegisterAsKnown(ContextIdProperty, (byte)51, DependencyProperty.PropertyValidity.Reexecute); 95 DependencyProperty.RegisterAsKnown(IsSuspensionRequestedProperty, (byte)52, DependencyProperty.PropertyValidity.Uninitialize); 96 DependencyProperty.RegisterAsKnown(TrackingCallingStateProperty, (byte)53, DependencyProperty.PropertyValidity.Uninitialize); 97 DependencyProperty.RegisterAsKnown(TrackingListenerBrokerProperty, (byte)54, DependencyProperty.PropertyValidity.Uninitialize); 98 DependencyProperty.RegisterAsKnown(IsIdleProperty, (byte)56, DependencyProperty.PropertyValidity.Uninitialize); 99 100 // registered by Scheduler 101 DependencyProperty.RegisterAsKnown(Scheduler.NormalPriorityEntriesQueueProperty, (byte)61, DependencyProperty.PropertyValidity.Uninitialize); 102 DependencyProperty.RegisterAsKnown(Scheduler.HighPriorityEntriesQueueProperty, (byte)62, DependencyProperty.PropertyValidity.Uninitialize); 103 104 // registered by other services 105 DependencyProperty.RegisterAsKnown(WorkflowQueuingService.LocalPersistedQueueStatesProperty, (byte)63, DependencyProperty.PropertyValidity.Reexecute); 106 DependencyProperty.RegisterAsKnown(WorkflowQueuingService.RootPersistedQueueStatesProperty, (byte)64, DependencyProperty.PropertyValidity.Reexecute); 107 DependencyProperty.RegisterAsKnown(CorrelationTokenCollection.CorrelationTokenCollectionProperty, (byte)65, DependencyProperty.PropertyValidity.Always); 108 DependencyProperty.RegisterAsKnown(CorrelationToken.NameProperty, (byte)67, DependencyProperty.PropertyValidity.Uninitialize); 109 DependencyProperty.RegisterAsKnown(CorrelationToken.OwnerActivityNameProperty, (byte)68, DependencyProperty.PropertyValidity.Uninitialize); 110 DependencyProperty.RegisterAsKnown(CorrelationToken.PropertiesProperty, (byte)69, DependencyProperty.PropertyValidity.Uninitialize); 111 DependencyProperty.RegisterAsKnown(CorrelationToken.SubscriptionsProperty, (byte)70, DependencyProperty.PropertyValidity.Uninitialize); 112 DependencyProperty.RegisterAsKnown(CorrelationToken.InitializedProperty, (byte)71, DependencyProperty.PropertyValidity.Uninitialize); 113 114 //registered by the definition dispenser 115 DependencyProperty.RegisterAsKnown(WorkflowDefinitionDispenser.WorkflowDefinitionHashCodeProperty, (byte)80, DependencyProperty.PropertyValidity.Reexecute); 116 117 118 // registered by workflow instance 119 DependencyProperty.RegisterAsKnown(WorkflowInstanceIdProperty, (byte)102, DependencyProperty.PropertyValidity.Reexecute); 120 DependencyProperty.RegisterAsKnown(IsBlockedProperty, (byte)103, DependencyProperty.PropertyValidity.Reexecute); 121 DependencyProperty.RegisterAsKnown(WorkflowStatusProperty, (byte)104, DependencyProperty.PropertyValidity.Reexecute); 122 DependencyProperty.RegisterAsKnown(SuspendOrTerminateInfoProperty, (byte)105, DependencyProperty.PropertyValidity.Reexecute); 123 124 workflowDebuggingDisabled = disableWorkflowDebugging.Enabled; 125 } 126 WorkflowExecutor(Guid instanceId)127 internal WorkflowExecutor(Guid instanceId) 128 { 129 this._isInstanceValid = false; 130 this._executorLock = LockFactory.CreateWorkflowExecutorLock(instanceId); 131 this._msgDeliveryLock = LockFactory.CreateWorkflowMessageDeliveryLock(instanceId); 132 this.stateChangedSincePersistence = true; 133 134 // If DisableWorkflowDebugging switch is turned off create WorkflowDebuggerService 135 if (!workflowDebuggingDisabled) 136 this._workflowDebuggerService = new WorkflowDebuggerService(this); 137 } 138 139 // Initialize for the root schedule Initialize(Activity rootActivity, WorkflowExecutor invokerExec, string invokeActivityID, Guid instanceId, IDictionary<string, object> namedArguments, WorkflowInstance workflowInstance)140 internal void Initialize(Activity rootActivity, WorkflowExecutor invokerExec, string invokeActivityID, Guid instanceId, IDictionary<string, object> namedArguments, WorkflowInstance workflowInstance) 141 { 142 this.rootActivity = rootActivity; 143 this.InstanceId = instanceId; 144 145 // Set the persisted State properties 146 this.rootActivity.SetValue(WorkflowExecutor.ContextIdProperty, 0); 147 this.rootActivity.SetValue(WorkflowInstanceIdProperty, instanceId); 148 this.WorkflowStatus = WorkflowStatus.Created; 149 this.rootActivity.SetValue(Activity.ActivityExecutionContextInfoProperty, new ActivityExecutionContextInfo(this.rootActivity.QualifiedName, GetNewContextId(), instanceId, -1)); 150 this.rootActivity.SetValue(Activity.ActivityContextGuidProperty, instanceId); 151 this.rootActivity.SetValue(WorkflowExecutor.IsIdleProperty, true); 152 this.isInstanceIdle = true; 153 154 // set workflow executor 155 this.rootActivity.SetValue(WorkflowExecutor.WorkflowExecutorProperty, this); 156 157 // initialize the root activity 158 RefreshWorkflowDefinition(); 159 Activity workflowDefinition = this.WorkflowDefinition; 160 if (workflowDefinition == null) 161 throw new InvalidOperationException("workflowDefinition"); 162 163 ((IDependencyObjectAccessor)this.rootActivity).InitializeActivatingInstanceForRuntime(null, this); 164 this.rootActivity.FixUpMetaProperties(workflowDefinition); 165 _runtime = workflowInstance.WorkflowRuntime; 166 167 if (invokerExec != null) 168 { 169 List<string> calleeBase = new List<string>(); 170 TrackingCallingState parentTCS = (TrackingCallingState)invokerExec.rootActivity.GetValue(WorkflowExecutor.TrackingCallingStateProperty); 171 if ((parentTCS != null) && (parentTCS.CallerActivityPathProxy != null)) 172 { 173 foreach (string qualifiedID in parentTCS.CallerActivityPathProxy) 174 calleeBase.Add(qualifiedID); 175 } 176 calleeBase.Add(invokeActivityID); 177 178 // 179 // This has been exec'd by another instance 180 // Set up tracking info to allow linking instances 181 Debug.Assert(invokeActivityID != null && invokeActivityID.Length > 0); 182 TrackingCallingState trackingCallingState = new TrackingCallingState(); 183 trackingCallingState.CallerActivityPathProxy = calleeBase; 184 trackingCallingState.CallerWorkflowInstanceId = invokerExec.InstanceId; 185 trackingCallingState.CallerContextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(invokerExec.CurrentActivity).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid; 186 if (null == invokerExec.CurrentActivity.Parent) 187 trackingCallingState.CallerParentContextGuid = trackingCallingState.CallerContextGuid; 188 else 189 trackingCallingState.CallerParentContextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(invokerExec.CurrentActivity.Parent).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid; 190 this.rootActivity.SetValue(WorkflowExecutor.TrackingCallingStateProperty, trackingCallingState); 191 } 192 193 _setInArgsOnCompanion(namedArguments); 194 195 this.schedulingContext = new Scheduler(this, true); 196 this._schedulerLock = LockFactory.CreateWorkflowSchedulerLock(this.InstanceId); 197 198 qService = new WorkflowQueuingService(this); 199 200 _workflowInstance = workflowInstance; 201 202 TimerQueue = new TimerEventSubscriptionCollection(this, this.InstanceId); 203 204 // register the dynamic activity 205 using (new ServiceEnvironment(this.rootActivity)) 206 { 207 using (SetCurrentActivity(this.rootActivity)) 208 { 209 this.RegisterDynamicActivity(this.rootActivity, false); 210 } 211 } 212 } 213 RegisterWithRuntime(WorkflowRuntime workflowRuntime)214 internal void RegisterWithRuntime(WorkflowRuntime workflowRuntime) 215 { 216 _isInstanceValid = true; 217 _runtime = workflowRuntime; 218 using (new ServiceEnvironment(this.rootActivity)) 219 { 220 using (SetCurrentActivity(this.rootActivity)) 221 { 222 using (ActivityExecutionContext executionContext = new ActivityExecutionContext(this.rootActivity, true)) 223 executionContext.InitializeActivity(this.rootActivity); 224 } 225 226 // 227 // Tell the runtime that the instance is ready 228 // so that internal components can set up event subscriptions 229 this._runtime.WorkflowExecutorCreated(this, false); 230 231 // 232 // Fire first events 233 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Creating); 234 } 235 } 236 237 // Used to recreate the root schedule executor from its persisted state Reload(Activity rootActivity, WorkflowInstance workflowInstance)238 internal void Reload(Activity rootActivity, WorkflowInstance workflowInstance) 239 { 240 _workflowInstance = workflowInstance; 241 ReloadHelper(rootActivity); 242 } 243 ReRegisterWithRuntime(WorkflowRuntime workflowRuntime)244 internal void ReRegisterWithRuntime(WorkflowRuntime workflowRuntime) 245 { 246 using (new SchedulerLockGuard(this._schedulerLock, this)) 247 { 248 _isInstanceValid = true; 249 _runtime = workflowRuntime; 250 using (new ServiceEnvironment(this.rootActivity)) 251 { 252 this._runtime.WorkflowExecutorCreated(this, true); 253 254 TimerQueue.Executor = this; 255 TimerQueue.ResumeDelivery(); 256 257 // This will get the instance running so do it last otherwise we can end up 258 // with ----s between the running workflow and deliverying timers, etc. 259 if (this.WorkflowStatus == WorkflowStatus.Running) 260 this.Scheduler.CanRun = true; 261 262 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Loading); 263 } 264 } 265 } 266 Registered(bool isActivation)267 internal void Registered(bool isActivation) 268 { 269 using (ScheduleWork work = new ScheduleWork(this)) 270 { 271 this.Scheduler.ResumeIfRunnable(); 272 } 273 if (isActivation) 274 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Created); 275 else 276 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Loaded); 277 } 278 279 // Used when replacing a workflow executor. Basically we move 280 // the locks from the previous executor so we guarantee that 281 // everything stays locks as it is supposed to be. Initialize(Activity rootActivity, WorkflowRuntime runtime, WorkflowExecutor previousWorkflowExecutor)282 internal void Initialize(Activity rootActivity, WorkflowRuntime runtime, WorkflowExecutor previousWorkflowExecutor) 283 { 284 _workflowInstance = previousWorkflowExecutor.WorkflowInstance; 285 ReloadHelper(rootActivity); 286 // mark instance as valid now 287 IsInstanceValid = true; 288 _runtime = runtime; 289 this._runtime.WorkflowExecutorCreated(this, true); 290 291 TimerQueue.Executor = this; 292 TimerQueue.ResumeDelivery(); 293 294 _executorLock = previousWorkflowExecutor._executorLock; 295 _msgDeliveryLock = previousWorkflowExecutor._msgDeliveryLock; 296 _schedulerLock = previousWorkflowExecutor._schedulerLock; 297 ScheduleWork.Executor = this; 298 } 299 300 // Used to recreate the root schedule executor from its persisted state ReloadHelper(Activity rootActivity)301 private void ReloadHelper(Activity rootActivity) 302 { 303 // assign activity state 304 this.rootActivity = rootActivity; 305 this.InstanceId = (Guid)rootActivity.GetValue(WorkflowInstanceIdProperty); 306 307 // set workflow executor 308 this.rootActivity.SetValue(WorkflowExecutor.WorkflowExecutorProperty, this); 309 310 this._schedulerLock = LockFactory.CreateWorkflowSchedulerLock(this.InstanceId); 311 312 this.schedulingContext = new Scheduler(this, false); 313 this.qService = new WorkflowQueuingService(this); 314 315 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Loading instance {0}", this.InstanceIdString); 316 DiagnosticStackTrace("load request"); 317 318 using (new ServiceEnvironment(this.rootActivity)) 319 { 320 321 // check if this instance can be loaded 322 switch (this.WorkflowStatus) 323 { 324 case WorkflowStatus.Completed: 325 case WorkflowStatus.Terminated: 326 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: attempt to load a completed/terminated instance: {0}", this.InstanceIdString); 327 throw new InvalidOperationException( 328 ExecutionStringManager.InvalidAttemptToLoad); 329 330 default: 331 break; 332 } 333 334 // new nonSerialized members 335 _resourceManager = new VolatileResourceManager(); 336 _runtime = _workflowInstance.WorkflowRuntime; 337 338 // register all dynamic activities for loading 339 Queue<Activity> dynamicActivitiesQueue = new Queue<Activity>(); 340 dynamicActivitiesQueue.Enqueue(this.rootActivity); 341 while (dynamicActivitiesQueue.Count > 0) 342 { 343 Activity dynamicActivity = dynamicActivitiesQueue.Dequeue(); 344 ((IDependencyObjectAccessor)dynamicActivity).InitializeInstanceForRuntime(this); 345 this.RegisterDynamicActivity(dynamicActivity, true); 346 347 IList<Activity> nestedDynamicActivities = (IList<Activity>)dynamicActivity.GetValue(Activity.ActiveExecutionContextsProperty); 348 if (nestedDynamicActivities != null) 349 { 350 foreach (Activity nestedDynamicActivity in nestedDynamicActivities) 351 dynamicActivitiesQueue.Enqueue(nestedDynamicActivity); 352 } 353 } 354 } 355 356 this.isInstanceIdle = (bool)this.rootActivity.GetValue(IsIdleProperty); 357 RefreshWorkflowDefinition(); 358 } 359 _setInArgsOnCompanion(IDictionary<string, object> namedInArguments)360 private void _setInArgsOnCompanion(IDictionary<string, object> namedInArguments) 361 { 362 // Do parameter property assignments. 363 if (namedInArguments != null) 364 { 365 foreach (string arg in namedInArguments.Keys) 366 { 367 368 PropertyInfo propertyInfo = this.WorkflowDefinition.GetType().GetProperty(arg); 369 370 if (propertyInfo != null && propertyInfo.CanWrite) 371 { 372 try 373 { 374 propertyInfo.SetValue(this.rootActivity, namedInArguments[arg], null); 375 } 376 catch (ArgumentException e) 377 { 378 throw new ArgumentException(ExecutionStringManager.InvalidWorkflowParameterValue, arg, e); 379 } 380 } 381 else 382 throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.SemanticErrorInvalidNamedParameter, ((Activity)this.WorkflowDefinition).Name, arg)); 383 } 384 } 385 } 386 #endregion Ctors 387 388 #region Misc properties and methods 389 390 internal TrackingCallingState TrackingCallingState 391 { 392 get 393 { 394 return (TrackingCallingState)this.rootActivity.GetValue(WorkflowExecutor.TrackingCallingStateProperty); 395 } 396 } 397 398 internal WorkflowRuntime WorkflowRuntime 399 { 400 get 401 { 402 return _runtime; 403 } 404 } 405 internal bool IsInstanceValid 406 { 407 get { return _isInstanceValid; } 408 set 409 { 410 if (!value) 411 { 412 this.ResourceManager.ClearAllBatchedWork(); 413 InstanceLock.AssertIsLocked(this._schedulerLock); 414 InstanceLock.AssertIsLocked(this._msgDeliveryLock); 415 } 416 _isInstanceValid = value; 417 } 418 } 419 420 internal bool IsIdle 421 { 422 get 423 { 424 return this.isInstanceIdle; 425 } 426 set 427 { 428 using (InstanceLock.InstanceLockGuard messageDeliveryLockGuard = this.MessageDeliveryLock.Enter()) 429 { 430 try 431 { 432 this.isInstanceIdle = value; 433 this.RootActivity.SetValue(WorkflowExecutor.IsIdleProperty, value); 434 } 435 finally 436 { 437 // Playing it safe here. If the try block throws, 438 // we test what was the resulting value of the 439 // property to see if we need to signal the 440 // waiting threads 441 if (this.IsIdle) 442 messageDeliveryLockGuard.Pulse(); 443 } 444 } 445 } 446 } 447 448 internal string AdditionalInformation 449 { 450 get { return (string)this.rootActivity.GetValue(SuspendOrTerminateInfoProperty); } 451 } 452 453 public WorkBatchCollection BatchCollection 454 { 455 get 456 { 457 return _resourceManager.BatchCollection; 458 } 459 } 460 461 internal VolatileResourceManager ResourceManager 462 { 463 get 464 { 465 return _resourceManager; 466 } 467 } 468 469 internal Activity WorkflowDefinition 470 { 471 get 472 { 473 Debug.Assert(_workflowDefinition != null, "WorkflowDefinition cannot be null."); 474 return _workflowDefinition; 475 } 476 } 477 RefreshWorkflowDefinition()478 private void RefreshWorkflowDefinition() 479 { 480 Activity tempDefinition = (Activity)this.rootActivity.GetValue(Activity.WorkflowDefinitionProperty); 481 Debug.Assert(tempDefinition != null, "WorkflowDefinition cannot be null."); 482 483 // Workflow definitions needs to have a locking object 484 // on them for use when cloning for public consumption 485 // (WorkflowInstance.GetWorkflowDefinition and 486 // WorkflowCompletedEventArgs.WorkflowDefinition). 487 WorkflowDefinitionLock.SetWorkflowDefinitionLockObject(tempDefinition, new object()); 488 489 _workflowDefinition = tempDefinition; 490 } 491 492 internal Activity RootActivity 493 { 494 get 495 { 496 return this.rootActivity; 497 } 498 } 499 500 internal Guid InstanceId 501 { 502 get 503 { 504 return workflowInstanceId; 505 } 506 private set 507 { 508 workflowInstanceId = value; 509 } 510 } 511 512 internal string InstanceIdString 513 { 514 get 515 { 516 if (workflowIdString == null) 517 workflowIdString = this.InstanceId.ToString(); 518 return workflowIdString; 519 } 520 } 521 522 523 internal InstanceLock MessageDeliveryLock 524 { 525 get 526 { 527 return _msgDeliveryLock; 528 } 529 } 530 531 internal InstanceLock ExecutorLock 532 { 533 get 534 { 535 return _executorLock; 536 } 537 } 538 539 internal WorkflowStateRollbackService WorkflowStateRollbackService 540 { 541 get 542 { 543 if (this.workflowStateRollbackService == null) 544 this.workflowStateRollbackService = new WorkflowStateRollbackService(this); 545 return this.workflowStateRollbackService; 546 } 547 } 548 549 internal WorkflowInstance WorkflowInstance 550 { 551 get 552 { 553 System.Diagnostics.Debug.Assert(this._workflowInstance != null, "WorkflowInstance property should not be called before the proxy is initialized."); 554 return this._workflowInstance; 555 } 556 } 557 Start()558 internal void Start() 559 { 560 using (ScheduleWork work = new ScheduleWork(this)) 561 { 562 using (this.ExecutorLock.Enter()) 563 { 564 if (this.WorkflowStatus != WorkflowStatus.Created) 565 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CannotStartInstanceTwice, this.InstanceId)); 566 567 // Set a new ServiceEnvironment to establish a current batch in TLS 568 // This is needed for synchronous status change notification at start 569 // (status init->executing) when there is no batch in TLS yet 570 // and there are subscribers like tracking 571 this.WorkflowStatus = WorkflowStatus.Running; 572 using (new ServiceEnvironment(this.rootActivity)) 573 { 574 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Starting); 575 try 576 { 577 using (ActivityExecutionContext executionContext = new ActivityExecutionContext(this.rootActivity, true)) 578 { 579 // make sure the scheduler is able to run 580 this.schedulingContext.CanRun = true; 581 582 // Since we are actually scheduling work at this point, we should grab 583 // the scheduler lock. This will avoid ----s some operations we schedule 584 // start executing before we are done scheduling all operations. 585 using (new SchedulerLockGuard(this._schedulerLock, this)) 586 { 587 executionContext.ExecuteActivity(this.rootActivity); 588 } 589 } 590 } 591 catch (Exception e) 592 { 593 Terminate(e.Message); 594 throw; 595 } 596 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Started); 597 598 } 599 } 600 } 601 } 602 603 internal Activity CurrentActivity 604 { 605 get { return _lastExecutingActivity; } 606 set { _lastExecutingActivity = value; } 607 } 608 609 internal Hashtable CompletedContextActivities 610 { 611 get { return this.completedContextActivities; } 612 set { this.completedContextActivities = value; } 613 } 614 615 GetNewContextId()616 private int GetNewContextId() 617 { 618 int conextId = (int)this.rootActivity.GetValue(WorkflowExecutor.ContextIdProperty) + 1; 619 this.rootActivity.SetValue(WorkflowExecutor.ContextIdProperty, conextId); 620 return conextId; 621 } 622 623 internal List<SchedulerLockGuardInfo> EventsToFireList 624 { 625 get 626 { 627 return eventsToFireList; 628 } 629 } 630 FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal, object eventInfo)631 private void FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal, object eventInfo) 632 { 633 eventsToFireList.Add(new SchedulerLockGuardInfo(this, workflowEventInternal, eventInfo)); 634 } 635 FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal)636 private void FireEventAfterSchedulerLockDrop(WorkflowEventInternal workflowEventInternal) 637 { 638 eventsToFireList.Add(new SchedulerLockGuardInfo(this, workflowEventInternal)); 639 } 640 641 #endregion Misc properties and methods 642 643 #region Scheduler Related 644 645 // asks the hosting env threadProvider for a thread ScheduleForWork()646 internal void ScheduleForWork() 647 { 648 this.IsIdle = false; 649 650 if (this.IsInstanceValid) 651 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Runnable); 652 653 ScheduleWork.NeedsService = true; 654 } 655 RequestHostingService()656 internal void RequestHostingService() 657 { 658 WorkflowRuntime.SchedulerService.Schedule(this.RunSome, this.InstanceId); 659 } 660 DeliverTimerSubscriptions()661 internal void DeliverTimerSubscriptions() 662 { 663 using (ScheduleWork work = new ScheduleWork(this)) 664 { 665 using (this._executorLock.Enter()) 666 { 667 if (this.IsInstanceValid) 668 { 669 using (this.MessageDeliveryLock.Enter()) 670 { 671 using (new ServiceEnvironment(this.rootActivity)) 672 { 673 if (!this.IsInstanceValid) 674 return; 675 676 TimerEventSubscriptionCollection queue = TimerQueue; 677 bool done = false; 678 while (!done) 679 { 680 lock (queue.SyncRoot) 681 { 682 TimerEventSubscription sub = queue.Peek(); 683 if (sub == null || sub.ExpiresAt > DateTime.UtcNow) 684 { 685 done = true; 686 } 687 else 688 { 689 WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Delivering timer subscription for instance {0}", this.InstanceIdString); 690 stateChangedSincePersistence = true; 691 lock (qService.SyncRoot) 692 { 693 if (qService.Exists(sub.QueueName)) 694 { 695 qService.EnqueueEvent(sub.QueueName, sub.SubscriptionId); 696 } 697 } 698 queue.Dequeue(); 699 } 700 } 701 } 702 } 703 } 704 } 705 } 706 } 707 } 708 709 // call from the threadProvider about the availability of a thread. RunSome(object ignored)710 internal void RunSome(object ignored) 711 { 712 using (ScheduleWork work = new ScheduleWork(this)) 713 { 714 using (new WorkflowTraceTransfer(this.InstanceId)) 715 { 716 using (new SchedulerLockGuard(this._schedulerLock, this)) 717 { 718 using (new ServiceEnvironment(this.rootActivity)) 719 { 720 // check if this is a valid in-memory instance 721 if (!this.IsInstanceValid) 722 return; 723 724 // check if instance already done 725 if ((this.rootActivity.ExecutionStatus == ActivityExecutionStatus.Closed) || (WorkflowStatus.Completed == this.WorkflowStatus) || (WorkflowStatus.Terminated == this.WorkflowStatus)) 726 return; 727 728 bool ignoreFinallyBlock = false; 729 730 // 731 // For V1 we don't support flow through transaction on the service thread 732 using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress)) 733 { 734 try 735 { 736 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Executing); 737 // run away ... run away... 738 this.RunScheduler(); 739 } 740 catch (Exception e) 741 { 742 if (WorkflowExecutor.IsIrrecoverableException(e)) 743 { 744 ignoreFinallyBlock = true; 745 throw; 746 } 747 else 748 { 749 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Fatal exception thrown in the scheduler. Terminating the workflow instance '{0}'. Exception:{1}\n{2}", this.InstanceIdString, e.Message, e.StackTrace); 750 this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e)); 751 this.ThrownException = e; 752 } 753 } 754 finally 755 { 756 if (!ignoreFinallyBlock) 757 { 758 FireWorkflowExecutionEvent(this, WorkflowEventInternal.NotExecuting); 759 } 760 } 761 scope.Complete(); 762 } 763 } 764 } 765 } 766 } 767 } 768 769 // this method is called with the scheduler lock held RunScheduler()770 private void RunScheduler() 771 { 772 InstanceLock.AssertIsLocked(this._schedulerLock); 773 774 // run away ... run away... 775 try 776 { 777 this.Scheduler.Run(); 778 } 779 finally 780 { 781 this.IsIdle = true; 782 } 783 784 if (!this.IsInstanceValid) 785 return; 786 787 if (this.WorkflowStateRollbackService.IsInstanceStateRevertRequested) 788 { 789 // 790 // Protect against message delivery while reverting 791 using (MessageDeliveryLock.Enter()) 792 { 793 this.WorkflowStateRollbackService.RevertToCheckpointState(); 794 return; 795 } 796 } 797 798 if (this.Scheduler.IsStalledNow) 799 { 800 // the instance has no ready work 801 802 // Protect against the host accessing DPs. 803 using (this.MessageDeliveryLock.Enter()) 804 { 805 if (this.rootActivity.ExecutionStatus != ActivityExecutionStatus.Closed) 806 { 807 this.ProcessQueuedEvents(); // deliver any outstanding queued events before persisting 808 if (this.Scheduler.IsStalledNow) 809 { 810 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: workflow instance '{0}' has no work.", this.InstanceIdString); 811 FireWorkflowExecutionEvent(this, WorkflowEventInternal.SchedulerEmpty); 812 813 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Idle); 814 815 WorkflowPersistenceService persistence = this.WorkflowRuntime.WorkflowPersistenceService; 816 817 // instance is not done.. must be idle 818 // can potentially dehydrate now.. 819 if ((persistence != null) && persistence.UnloadOnIdle(this.rootActivity)) 820 { 821 if (!this.IsInstanceValid) 822 return; 823 824 // Do not unload if we are not unloadable and if a persistence exception 825 // was thrown the last time. 826 if (this.IsUnloadableNow && !(this.ThrownException is PersistenceException)) 827 { 828 PerformUnloading(true); 829 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "WorkflowExecutor: unloaded workflow instance '{0}'. IsInstanceValid={1}", this.InstanceIdString, IsInstanceValid); 830 } 831 } 832 else 833 { 834 if (this.ResourceManager.IsBatchDirty && this.currentAtomicActivity == null) 835 { 836 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: workflow instance '{0}' has no work and the batch is dirty. Persisting state and commiting batch.", this.InstanceIdString); 837 this.Persist(this.rootActivity, false, false); 838 } 839 } 840 } 841 } 842 } 843 } 844 else 845 { 846 // the instance has ready work but was told to stop 847 // 848 849 // if suspension was requested, suspend now. 850 851 if ((bool)this.rootActivity.GetValue(WorkflowExecutor.IsSuspensionRequestedProperty)) 852 { 853 this.SuspendOnIdle(this.AdditionalInformation); 854 this.rootActivity.SetValue(WorkflowExecutor.IsSuspensionRequestedProperty, false); 855 } 856 } 857 858 if (this.currentAtomicActivity != null) 859 { 860 // Leave TransactionScope before giving up the thread 861 TransactionalProperties transactionalProperties = (TransactionalProperties)this.currentAtomicActivity.GetValue(TransactionalPropertiesProperty); 862 DisposeTransactionScope(transactionalProperties); 863 } 864 } 865 866 private bool attemptedRootAECUnload = false; 867 private bool attemptedRootDispose = false; 868 DisposeRootActivity(bool aborting)869 private void DisposeRootActivity(bool aborting) 870 { 871 try 872 { 873 if (!attemptedRootAECUnload) 874 { 875 attemptedRootAECUnload = true; 876 this.RootActivity.OnActivityExecutionContextUnload(this); 877 } 878 if (!attemptedRootDispose) 879 { 880 attemptedRootDispose = true; 881 this.RootActivity.Dispose(); 882 } 883 } 884 catch (Exception) 885 { 886 if (!aborting) 887 { 888 using (_msgDeliveryLock.Enter()) 889 { 890 this.AbortOnIdle(); 891 throw; 892 } 893 } 894 } 895 } 896 897 898 internal Scheduler Scheduler 899 { 900 get 901 { 902 return this.schedulingContext; 903 } 904 } 905 906 #endregion Scheduler Related 907 908 #region IInstanceState 909 910 /// <summary> 911 /// Instance Id 912 /// </summary> 913 /// <value></value> 914 internal Guid ID 915 { 916 get { return InstanceId; } 917 } 918 919 /// <summary> 920 /// Completed status for instances clean up 921 /// </summary> 922 /// <value></value> 923 internal WorkflowStatus WorkflowStatus 924 { 925 get { return (WorkflowStatus)this.rootActivity.GetValue(WorkflowStatusProperty); } 926 private set { this.rootActivity.SetValue(WorkflowStatusProperty, value); } 927 } 928 929 internal TimerEventSubscriptionCollection TimerQueue 930 { 931 get 932 { 933 if (_timerQueue == null) 934 { 935 _timerQueue = (TimerEventSubscriptionCollection)this.rootActivity.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty); 936 Debug.Assert(_timerQueue != null, "TimerEventSubscriptionCollection on root activity should never be null, but it was"); 937 } 938 return _timerQueue; 939 } 940 private set 941 { 942 _timerQueue = value; 943 this.rootActivity.SetValue(TimerEventSubscriptionCollection.TimerCollectionProperty, _timerQueue); 944 } 945 } 946 947 948 #endregion 949 950 #region Persistence 951 ProtectedPersist(bool unlock)952 private bool ProtectedPersist(bool unlock) 953 { 954 try 955 { 956 // persist 957 this.Persist(this.rootActivity, unlock, false); 958 } 959 catch (Exception e) 960 { 961 if (WorkflowExecutor.IsIrrecoverableException(e)) 962 { 963 throw; 964 } //@@undone: for Microsoft:- we should not be running exception handler, when we are unlocking. 965 else if (this.WorkflowStatus != WorkflowStatus.Suspended && this.IsInstanceValid) 966 { 967 // the persistence attempt threw an exception 968 // lets give this exception to a scope 969 Activity activity = FindExecutorToHandleException(); 970 971 this.Scheduler.CanRun = true; 972 this.ExceptionOccured(e, activity, null); 973 } 974 else 975 { 976 if (this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e))) 977 { 978 this.stateChangedSincePersistence = true; 979 this.WorkflowStatus = WorkflowStatus.Terminated; 980 } 981 } 982 return false; 983 } 984 return true; 985 } 986 FindExecutorToHandleException()987 private Activity FindExecutorToHandleException() 988 { 989 Activity lastExecutingActivity = this.CurrentActivity; 990 if (lastExecutingActivity == null) 991 lastExecutingActivity = this.rootActivity; 992 return lastExecutingActivity; 993 } 994 995 // called by core runtime to persist the instance. 996 // 'exec' is the executor requesting the persistence Persist(Activity dynamicActivity, bool unlock, bool needsCompensation)997 internal void Persist(Activity dynamicActivity, bool unlock, bool needsCompensation) 998 { 999 InstanceLock.AssertIsLocked(this._schedulerLock); 1000 Activity currentActivity = (this.CurrentActivity == null) ? dynamicActivity : this.CurrentActivity; 1001 // 1002 // Save the current status. The status may change in PrePersist 1003 // and we need to reset if the commit fails for any reason. 1004 WorkflowStatus oldStatus = this.WorkflowStatus; 1005 1006 // New a ServiceEnvironment to set the current batch to be of the exec to be persisted 1007 using (new ServiceEnvironment(currentActivity)) 1008 { 1009 try 1010 { 1011 // prevent the message delivery from outside 1012 using (this.MessageDeliveryLock.Enter()) 1013 { 1014 this.ProcessQueuedEvents(); // Must always process this queue before persisting state! 1015 // check what has changed since last persist 1016 // 1017 if (this.ResourceManager.IsBatchDirty) 1018 { 1019 // if there is work in the batch, persist the state to be consistent 1020 this.stateChangedSincePersistence = true; 1021 } 1022 else 1023 { 1024 // no work in the batch... 1025 if (!this.stateChangedSincePersistence && !unlock) 1026 { 1027 // the instance state is not dirty 1028 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: NOT Persisting Instance '{0}' since the batch is NOT dirty and the instance state is NOT dirty", this.InstanceIdString); 1029 return; 1030 } 1031 } 1032 1033 // prepare the state for persistence 1034 // 1035 this.PrePersist(); 1036 1037 if (WorkflowStatus.Completed == WorkflowStatus) 1038 { 1039 // Any remaining messages in queues are zombie messages so move all to the pending queue 1040 this.qService.MoveAllMessagesToPendingQueue(); 1041 } 1042 // give the state to the persistence provider 1043 WorkflowPersistenceService persistence = this.WorkflowRuntime.WorkflowPersistenceService; 1044 1045 // Create a transient batch for Persistence Service. 1046 currentActivity.SetValue(TransientBatchProperty, _resourceManager.BatchCollection.GetTransientBatch()); 1047 1048 bool firedPersistingEvent = false; 1049 1050 if (persistence != null) 1051 { 1052 foreach (Activity completedContextActivity in this.completedContextActivities.Values) 1053 { 1054 // Save the committing activity 1055 completedContextActivity.SetValue(WorkflowInstanceIdProperty, this.InstanceId); 1056 1057 if (!firedPersistingEvent) 1058 { 1059 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Persisting); 1060 firedPersistingEvent = true; 1061 } 1062 1063 persistence.SaveCompletedContextActivity(completedContextActivity); 1064 completedContextActivity.Dispose(); 1065 } 1066 1067 if (this.stateChangedSincePersistence) 1068 { 1069 if (!firedPersistingEvent) 1070 { 1071 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Persisting); 1072 firedPersistingEvent = true; 1073 } 1074 1075 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Calling SaveWorkflowInstanceState for instance {0} hc {1}", this.InstanceIdString, this.GetHashCode()); 1076 persistence.SaveWorkflowInstanceState(this.rootActivity, unlock); 1077 } 1078 else if (unlock) 1079 { 1080 persistence.UnlockWorkflowInstanceState(this.rootActivity); 1081 } 1082 } 1083 1084 if (unlock) 1085 { 1086 DisposeRootActivity(false); 1087 } 1088 1089 // commit 1090 // check batch again, since the persistence provider may have added something. 1091 // If we are unlocking (unloading/dehydrating) commit the batch 1092 // regardless of whether the batch items signal that they need a commit 1093 if (this.currentAtomicActivity != null || this.ResourceManager.IsBatchDirty || (unlock && HasNonEmptyWorkBatch())) 1094 { 1095 1096 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Calling CommitTransaction for instance {0} hc {1}", this.InstanceIdString, this.GetHashCode()); 1097 this.CommitTransaction(currentActivity); 1098 } 1099 1100 if (firedPersistingEvent) 1101 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Persisted); 1102 1103 // post-persist 1104 // 1105 this.stateChangedSincePersistence = false; 1106 this.PostPersist(); 1107 // 1108 // Must do this after all persist related work has successfully finished 1109 // If we weren't successful we aren't actually completed 1110 if (WorkflowStatus.Completed == WorkflowStatus) 1111 { 1112 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Completed); 1113 this.IsInstanceValid = false; 1114 } 1115 } 1116 } 1117 catch (PersistenceException e) 1118 { 1119 this.Rollback(oldStatus); 1120 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Persist attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 1121 throw; 1122 } 1123 catch (Exception e) 1124 { 1125 if (WorkflowExecutor.IsIrrecoverableException(e)) 1126 { 1127 throw; 1128 } 1129 this.Rollback(oldStatus); 1130 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Persist attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 1131 throw new PersistenceException(e.Message, e); 1132 } 1133 finally 1134 { 1135 //Flush the transient Batch 1136 currentActivity.SetValue(TransientBatchProperty, null); 1137 } 1138 } 1139 } 1140 1141 /// <summary> 1142 /// There is always at least 1 BatchCollection (at root), 1143 /// check if any batch contains any work item 1144 /// </summary> 1145 /// <returns></returns> HasNonEmptyWorkBatch()1146 private bool HasNonEmptyWorkBatch() 1147 { 1148 foreach (WorkBatch workBatch in ResourceManager.BatchCollection.Values) 1149 { 1150 if (workBatch.Count > 0) 1151 return true; 1152 } 1153 return false; 1154 } 1155 1156 /// <summary> 1157 /// PrePersist 1158 /// 1159 /// Signal to prepare the state for persistence. 1160 /// </summary> PrePersist()1161 private void PrePersist() 1162 { 1163 // 1164 // This is our hook to set the workflowstatus to Completed 1165 // so that it is correctly written to persistence 1166 WorkflowStatus workflowStatus = this.WorkflowStatus; 1167 if ((ActivityExecutionStatus.Closed == this.rootActivity.ExecutionStatus) && (WorkflowStatus.Terminated != workflowStatus)) 1168 { 1169 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Completing); 1170 this.WorkflowStatus = WorkflowStatus.Completed; 1171 } 1172 1173 switch (this.WorkflowStatus) 1174 { 1175 case WorkflowStatus.Running: 1176 this.rootActivity.SetValue(IsBlockedProperty, this.Scheduler.IsStalledNow); 1177 break; 1178 case WorkflowStatus.Suspended: 1179 case WorkflowStatus.Completed: 1180 case WorkflowStatus.Terminated: 1181 case WorkflowStatus.Created: 1182 this.rootActivity.SetValue(IsBlockedProperty, false); 1183 break; 1184 default: 1185 Debug.Assert(false, "Unknown WorkflowStatus"); 1186 break; 1187 } 1188 1189 qService.PrePersist(); 1190 } 1191 PostPersist()1192 private void PostPersist() 1193 { 1194 qService.PostPersist(true); 1195 if (this.Scheduler != null) 1196 this.Scheduler.PostPersist(); 1197 this.completedContextActivities.Clear(); 1198 } 1199 Rollback(WorkflowStatus oldStatus)1200 private void Rollback(WorkflowStatus oldStatus) 1201 { 1202 this.WorkflowStatus = oldStatus; 1203 1204 if (this.Scheduler != null) 1205 this.Scheduler.Rollback(); 1206 } 1207 1208 #endregion 1209 1210 #region MessageArrival and Query 1211 ProcessQueuedEvents()1212 internal void ProcessQueuedEvents() 1213 { 1214 using (MessageDeliveryLock.Enter()) 1215 { 1216 qService.ProcessesQueuedAsynchronousEvents(); 1217 } 1218 } 1219 EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, Object workItem)1220 internal void EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, Object workItem) 1221 { 1222 using (ScheduleWork work = new ScheduleWork(this)) 1223 { 1224 bool lockedScheduler = false; 1225 if (!ServiceEnvironment.IsInServiceThread(InstanceId)) 1226 lockedScheduler = _schedulerLock.TryEnter(); 1227 try 1228 { 1229 // take the msg delivery lock to make sure the instance 1230 // doesn't persist while the message is being delivered. 1231 using (this.MessageDeliveryLock.Enter()) 1232 { 1233 if (!this.IsInstanceValid) 1234 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1235 1236 if (lockedScheduler || ServiceEnvironment.IsInServiceThread(InstanceId)) 1237 { 1238 using (new ServiceEnvironment(this.RootActivity)) 1239 { 1240 qService.EnqueueEvent(queueName, item); 1241 } 1242 } 1243 else 1244 { 1245 if (qService.SafeEnqueueEvent(queueName, item)) 1246 { 1247 ScheduleWork.NeedsService = true; 1248 } 1249 } 1250 1251 // add work items to the current batch if exists 1252 if (pendingWork != null) 1253 { 1254 IWorkBatch batch = _resourceManager.BatchCollection.GetBatch(this.rootActivity); 1255 batch.Add(pendingWork, workItem); 1256 } 1257 1258 stateChangedSincePersistence = true; 1259 } 1260 } 1261 finally 1262 { 1263 if (lockedScheduler) 1264 _schedulerLock.Exit(); 1265 } 1266 } 1267 } 1268 EnqueueItemOnIdle(IComparable queueName, object item, IPendingWork pendingWork, Object workItem)1269 internal void EnqueueItemOnIdle(IComparable queueName, object item, IPendingWork pendingWork, Object workItem) 1270 { 1271 using (ScheduleWork work = new ScheduleWork(this)) 1272 { 1273 // prevent other control operations from outside 1274 using (this._executorLock.Enter()) 1275 { 1276 if (!this.IsInstanceValid) 1277 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1278 1279 // take the msg delivery lock to make sure the instance 1280 // doesn't persist while the message is being delivered. 1281 using (InstanceLock.InstanceLockGuard messageDeliveryLockGuard = this.MessageDeliveryLock.Enter()) 1282 { 1283 using (new ServiceEnvironment(this.rootActivity)) 1284 { 1285 1286 if (!this.IsInstanceValid) 1287 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1288 1289 // Wait until the Scheduler is idle. 1290 while (!this.IsIdle) 1291 { 1292 messageDeliveryLockGuard.Wait(); 1293 if (!this.IsInstanceValid) 1294 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1295 } 1296 1297 // At this point the scheduler is not running and it is 1298 // EnqueueItemOnIdle is not valid for suspended workflows 1299 if ((this.WorkflowStatus == WorkflowStatus.Suspended) || (!this.Scheduler.CanRun)) 1300 throw new InvalidOperationException(ExecutionStringManager.InvalidWaitForIdleOnSuspendedWorkflow); 1301 1302 try 1303 { 1304 // add work items to the current batch if exists 1305 if (pendingWork != null) 1306 { 1307 IWorkBatch batch = (IWorkBatch)this.rootActivity.GetValue(WorkflowExecutor.TransientBatchProperty); 1308 batch.Add(pendingWork, workItem); 1309 } 1310 1311 stateChangedSincePersistence = true; 1312 qService.EnqueueEvent(queueName, item); 1313 } 1314 finally 1315 { 1316 if (this.IsIdle) 1317 messageDeliveryLockGuard.Pulse(); 1318 } 1319 } 1320 } 1321 } 1322 } 1323 } 1324 GetWorkflowQueueInfos()1325 internal ReadOnlyCollection<WorkflowQueueInfo> GetWorkflowQueueInfos() 1326 { 1327 List<WorkflowQueueInfo> queuedItems = new List<WorkflowQueueInfo>(); 1328 // take the msg delivery lock to make sure the queues don't 1329 // change during the list assembly. 1330 using (this.MessageDeliveryLock.Enter()) 1331 { 1332 using (new ServiceEnvironment(this.rootActivity)) 1333 { 1334 lock (qService.SyncRoot) 1335 { 1336 IEnumerable<IComparable> names = qService.QueueNames; 1337 foreach (IComparable name in names) 1338 { 1339 try 1340 { 1341 WorkflowQueue queue = qService.GetWorkflowQueue(name); 1342 if (!queue.Enabled) 1343 continue; 1344 Queue items = qService.GetQueue(name).Messages; 1345 List<ActivityExecutorDelegateInfo<QueueEventArgs>> listeners = qService.GetQueue(name).AsynchronousListeners; 1346 List<string> subscribedActivities = new List<string>(); 1347 foreach (ActivityExecutorDelegateInfo<QueueEventArgs> l in listeners) 1348 { 1349 string activity = (l.SubscribedActivityQualifiedName == null) ? l.ActivityQualifiedName : l.SubscribedActivityQualifiedName; 1350 subscribedActivities.Add(activity); 1351 } 1352 queuedItems.Add(new WorkflowQueueInfo(name, items, subscribedActivities.AsReadOnly())); 1353 } 1354 catch (InvalidOperationException) 1355 { 1356 // ignore this queue if it has disappeared 1357 } 1358 } 1359 } 1360 } 1361 } 1362 return queuedItems.AsReadOnly(); 1363 } 1364 GetWorkflowNextTimerExpiration()1365 internal DateTime GetWorkflowNextTimerExpiration() 1366 { 1367 using (this._executorLock.Enter()) 1368 { 1369 using (this.MessageDeliveryLock.Enter()) 1370 { 1371 TimerEventSubscriptionCollection timers = TimerQueue; 1372 TimerEventSubscription sub = timers.Peek(); 1373 return sub == null ? DateTime.MaxValue : sub.ExpiresAt; 1374 } 1375 } 1376 } 1377 1378 #endregion MessageArrival and Query 1379 1380 #region executor to execution context mappings 1381 1382 //This list is populated at loading time. 1383 //a map of SubState Tracking Context - SubState. 1384 [NonSerialized] 1385 private Dictionary<int, Activity> subStateMap = new Dictionary<int, Activity>(); 1386 RegisterDynamicActivity(Activity dynamicActivity, bool load)1387 internal void RegisterDynamicActivity(Activity dynamicActivity, bool load) 1388 { 1389 int contextId = ContextActivityUtils.ContextId(dynamicActivity); 1390 this.subStateMap.Add(contextId, dynamicActivity); 1391 1392 System.Workflow.Runtime.WorkflowTrace.Runtime.TraceEvent( 1393 TraceEventType.Information, 0, "Adding context {0}:{1}", 1394 contextId, dynamicActivity.QualifiedName + (load ? " for load" : "")); 1395 1396 dynamicActivity.OnActivityExecutionContextLoad(this); 1397 } 1398 UnregisterDynamicActivity(Activity dynamicActivity)1399 internal void UnregisterDynamicActivity(Activity dynamicActivity) 1400 { 1401 int contextId = ContextActivityUtils.ContextId(dynamicActivity); 1402 this.subStateMap.Remove(contextId); 1403 1404 System.Workflow.Runtime.WorkflowTrace.Runtime.TraceEvent( 1405 TraceEventType.Information, 0, "Removing context {0}:{1}", 1406 contextId, dynamicActivity.QualifiedName); 1407 1408 dynamicActivity.OnActivityExecutionContextUnload(this); 1409 } 1410 GetContextActivityForId(int stateId)1411 internal Activity GetContextActivityForId(int stateId) 1412 { 1413 if (this.subStateMap.ContainsKey(stateId)) 1414 return this.subStateMap[stateId]; 1415 return null; 1416 } 1417 1418 #endregion 1419 1420 #region Unloading 1421 // indicates whether an this schedule instance can be unloaded right now 1422 internal bool IsUnloadableNow 1423 { 1424 // Called by hosting environment 1425 get { return ((this.currentAtomicActivity == null) && (this.Scheduler.IsStalledNow || this.WorkflowStatus == WorkflowStatus.Suspended)); } 1426 } 1427 1428 /// <summary> 1429 /// Synchronously unload if currently idle 1430 /// </summary> 1431 /// <returns>true if successful</returns> TryUnload()1432 internal bool TryUnload() 1433 { 1434 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a TryUnload request for instance {0}", this.InstanceIdString); 1435 DiagnosticStackTrace("try unload request"); 1436 1437 try 1438 { 1439 // check if this is a valid in-memory instance 1440 if (!this.IsInstanceValid) 1441 return false; 1442 1443 // check if there is a persistence service 1444 if (this.WorkflowRuntime.WorkflowPersistenceService == null) 1445 { 1446 string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId); 1447 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg); 1448 throw new InvalidOperationException(errMsg); 1449 } 1450 using (new ScheduleWork(this, true)) 1451 { 1452 // Stop threads from outside - message delivery and control operations 1453 if (this._executorLock.TryEnter()) 1454 { 1455 try 1456 { 1457 // we need to take these locks to make sure that we have a fixed picture of the 1458 // unloadability state of the workflow. 1459 if (this._schedulerLock.TryEnter()) 1460 { 1461 try 1462 { 1463 if (this._msgDeliveryLock.TryEnter()) 1464 { 1465 using (new ServiceEnvironment(this.rootActivity)) 1466 { 1467 try 1468 { 1469 if (!this.IsInstanceValid) 1470 return false; 1471 1472 this.ProcessQueuedEvents(); // deliver any outstanding queued events before persisting 1473 if (this.IsUnloadableNow) 1474 { 1475 // can unload now 1476 return PerformUnloading(false); 1477 } 1478 else 1479 return false; 1480 } 1481 finally 1482 { 1483 this._msgDeliveryLock.Exit(); 1484 } 1485 } 1486 } 1487 } 1488 finally 1489 { 1490 SchedulerLockGuard.Exit(this._schedulerLock, this); 1491 } 1492 } 1493 } 1494 finally 1495 { 1496 this._executorLock.Exit(); 1497 } 1498 } 1499 } 1500 } 1501 catch (Exception e) 1502 { 1503 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: TryUnloading attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 1504 throw; 1505 } 1506 return false; 1507 } 1508 1509 // this unloads the instance by assuming that it can be unloaded. PerformUnloading(bool handleExceptions)1510 private bool PerformUnloading(bool handleExceptions) 1511 { 1512 InstanceLock.AssertIsLocked(this._schedulerLock); 1513 1514 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Unloading instance {0}", this.InstanceIdString); 1515 DiagnosticStackTrace("unload request"); 1516 1517 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Unloading); 1518 // 1519 // Block message delivery for duration of persist and marking as invalid 1520 using (_msgDeliveryLock.Enter()) 1521 { 1522 TimerQueue.SuspendDelivery(); 1523 1524 bool persisted; 1525 if (handleExceptions) 1526 { 1527 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling PerformUnloading(false): InstanceId {0}, hc: {1}", InstanceIdString, this.GetHashCode()); 1528 persisted = this.ProtectedPersist(true); 1529 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from ProtectedPersist: InstanceId {0}, hc: {1}, ret={2}", InstanceIdString, this.GetHashCode(), persisted); 1530 } 1531 else 1532 { 1533 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling Persist"); 1534 this.Persist(this.rootActivity, true, false); 1535 persisted = true; 1536 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from Persist: InstanceId {0}, hc: {1}, IsInstanceValid={2}", InstanceIdString, this.GetHashCode(), IsInstanceValid); 1537 } 1538 if (persisted) 1539 { 1540 // mark instance as invalid 1541 this.IsInstanceValid = false; 1542 1543 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Unloaded); 1544 return true; 1545 } 1546 else 1547 return false; 1548 } 1549 } 1550 1551 // shutsdown the schedule instance sync Unload()1552 internal void Unload() 1553 { 1554 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got an unload request for instance {0}", this.InstanceIdString); 1555 DiagnosticStackTrace("unload request"); 1556 1557 try 1558 { 1559 using (new ScheduleWork(this, true)) 1560 { 1561 // Stop threads from outside - message delivery and control operations 1562 using (this._executorLock.Enter()) 1563 { 1564 if (this.WorkflowRuntime.WorkflowPersistenceService == null) 1565 { 1566 string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId); 1567 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg); 1568 throw new InvalidOperationException(errMsg); 1569 } 1570 1571 // tell the scheduler to stop running 1572 this.Scheduler.CanRun = false; 1573 // If there were some thread executing the instance, then setting up 1574 // the callback, the thread getting done and the notification coming back 1575 // is racy... so we lock the scheduler 1576 using (new SchedulerLockGuard(this._schedulerLock, this)) 1577 { 1578 using (new ServiceEnvironment(this.rootActivity)) 1579 { 1580 // check if this is a valid in-memory instance 1581 if (!this.IsInstanceValid) 1582 { 1583 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1584 } 1585 1586 // the scheduler must be idle now 1587 if (this.currentAtomicActivity == null) 1588 { 1589 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Calling PerformUnloading(false) on instance {0} hc {1}", InstanceIdString, this.GetHashCode()); 1590 // unload 1591 PerformUnloading(false); 1592 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, InstanceId + ": Returning from PerformUnloading(false): IsInstanceValue: " + IsInstanceValid); 1593 } 1594 else 1595 { 1596 this.Scheduler.CanRun = true; 1597 throw new ExecutorLocksHeldException(atomicActivityEvent); 1598 } 1599 } 1600 } 1601 } 1602 } 1603 } 1604 catch (Exception e) 1605 { 1606 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Unload attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 1607 throw; 1608 } 1609 } 1610 1611 #endregion 1612 1613 #region Terminate 1614 1615 // terminates the schedule instance sync 1616 // must be called only from outside the instance... the thread running the instance must 1617 // never call this method... it should call TerminateOnIdle instead. Terminate(string error)1618 internal void Terminate(string error) 1619 { 1620 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor::Terminate : Got a terminate request for instance {0}", this.InstanceIdString); 1621 1622 try 1623 { 1624 using (new ScheduleWork(this, true)) 1625 { 1626 // Stop threads from outside - message delivery and control operations 1627 using (this._executorLock.Enter()) 1628 { 1629 // tell the scheduler to stop returnig items from its queue (ref: 16534) 1630 this.Scheduler.AbortOrTerminateRequested = true; 1631 // tell the scheduler to stop running 1632 this.Scheduler.CanRun = false; 1633 1634 // If there were some thread executing the instance, then setting up 1635 // the callback, the thread getting done and the notification coming back 1636 // is racy... so we lock the scheduler 1637 using (new SchedulerLockGuard(this._schedulerLock, this)) 1638 { 1639 using (new ServiceEnvironment(this.rootActivity)) 1640 { 1641 1642 // check if this is a valid in-memory instance 1643 if (!this.IsInstanceValid) 1644 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1645 1646 this.TerminateOnIdle(error); 1647 } 1648 } 1649 } 1650 } 1651 } 1652 catch (Exception e) 1653 { 1654 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Terminate attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 1655 throw; 1656 } 1657 } 1658 1659 // this method must be called with the scheduler lock held TerminateOnIdle(string error)1660 internal bool TerminateOnIdle(string error) 1661 { 1662 InstanceLock.AssertIsLocked(this._schedulerLock); 1663 1664 // check if the instance can be terminated 1665 if (!this.IsInstanceValid) 1666 return false; 1667 1668 // tell the scheduler to stop running 1669 this.Scheduler.CanRun = false; 1670 1671 try 1672 { 1673 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Terminating instance {0}", this.InstanceIdString); 1674 1675 if (null != ThrownException) 1676 FireWorkflowTerminating(ThrownException); 1677 else 1678 FireWorkflowTerminating(error); 1679 1680 1681 // mark instance as canceled 1682 this.stateChangedSincePersistence = true; 1683 WorkflowStatus oldStatus = this.WorkflowStatus; 1684 this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, error); 1685 this.WorkflowStatus = WorkflowStatus.Terminated; 1686 // 1687 // Block message delivery for duration of persistence and marking as invalid instance 1688 using (_msgDeliveryLock.Enter()) 1689 { 1690 TimerQueue.SuspendDelivery(); 1691 this.rootActivity.SetValue(Activity.ExecutionResultProperty, ActivityExecutionResult.Canceled); 1692 try 1693 { 1694 // persist the instance state 1695 this.Persist(this.rootActivity, true, false); 1696 } 1697 catch (Exception e) 1698 { 1699 // the persistence at terminate threw an exception. 1700 this.WorkflowStatus = oldStatus; 1701 this.rootActivity.SetValue(Activity.ExecutionResultProperty, ActivityExecutionResult.None); 1702 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Persistence attempt at instance '{0}' termination threw an exception. Aborting the instance. The termination event would be raised. The instance would execute from the last persisted point whenever started by the host explicitly. Exception:{1}\n{2}", this.InstanceIdString, e.Message, e.StackTrace); 1703 this.AbortOnIdle(); 1704 return false; 1705 } 1706 1707 // Any remaining messages in queues are zombie messages so move all to the pending queue 1708 this.qService.MoveAllMessagesToPendingQueue(); 1709 1710 if (null != ThrownException) 1711 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Terminated, ThrownException); 1712 else 1713 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Terminated, error); 1714 1715 // unsubscribe for model changes 1716 Debug.Assert(this.IsInstanceValid); 1717 // mark instance as invalid 1718 this.IsInstanceValid = false; 1719 } 1720 1721 if (currentAtomicActivity != null) 1722 { 1723 atomicActivityEvent.Set(); 1724 atomicActivityEvent.Close(); 1725 } 1726 } 1727 catch (Exception) 1728 { 1729 if ((this.rootActivity == this.CurrentActivity) && this.rootActivity.ExecutionStatus == ActivityExecutionStatus.Closed) 1730 { 1731 using (_msgDeliveryLock.Enter()) 1732 { 1733 this.AbortOnIdle(); 1734 return false; 1735 } 1736 } 1737 else 1738 { 1739 this.Scheduler.CanRun = true; 1740 throw; 1741 } 1742 } 1743 1744 return true; 1745 } 1746 1747 #endregion 1748 1749 #region Abort 1750 1751 // aborts the schedule instance sync 1752 // must be called only from outside the instance... the thread running the instance must 1753 // never call this method... it should call AbortOnIdle instead. Abort()1754 internal void Abort() 1755 { 1756 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor::Abort : Got a abort request for instance {0}", this.InstanceIdString); 1757 1758 try 1759 { 1760 // Stop threads from outside - message delivery and control operations 1761 using (this._executorLock.Enter()) 1762 { 1763 // tell the scheduler to stop returnig items from its queue (ref: 16534) 1764 this.Scheduler.AbortOrTerminateRequested = true; 1765 // tell the scheduler to stop running 1766 this.Scheduler.CanRun = false; 1767 1768 // If there were some thread executing the instance, then setting up 1769 // the callback, the thread getting done and the notification coming back 1770 // is racy... so we lock the scheduler 1771 using (new SchedulerLockGuard(this._schedulerLock, this)) 1772 { 1773 using (this._msgDeliveryLock.Enter()) 1774 { 1775 using (new ServiceEnvironment(this.rootActivity)) 1776 { 1777 1778 // check if this is a valid in-memory instance 1779 if (!this.IsInstanceValid) 1780 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1781 1782 1783 this.AbortOnIdle(); 1784 } 1785 } 1786 } 1787 } 1788 } 1789 catch (Exception e) 1790 { 1791 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Abort attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 1792 throw; 1793 } 1794 } 1795 1796 // this method must be called with the scheduler lock held AbortOnIdle()1797 internal void AbortOnIdle() 1798 { 1799 InstanceLock.AssertIsLocked(this._schedulerLock); 1800 InstanceLock.AssertIsLocked(this._msgDeliveryLock); 1801 1802 // check if the instance can be aborted 1803 if (!this.IsInstanceValid) 1804 return; 1805 1806 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Aborting); 1807 1808 TimerQueue.SuspendDelivery(); 1809 1810 // tell the scheduler to stop running 1811 this.Scheduler.CanRun = false; 1812 1813 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Aborting instance {0}", this.InstanceIdString); 1814 1815 try 1816 { 1817 // abort any transaction in progress 1818 if (this.currentAtomicActivity != null) 1819 { 1820 this.RollbackTransaction(null, this.currentAtomicActivity); 1821 this.currentAtomicActivity = null; 1822 } 1823 1824 // clear the batched work 1825 this.ResourceManager.ClearAllBatchedWork(); 1826 1827 // unlock instance state w/o saving it 1828 WorkflowPersistenceService persistenceSvc = this.WorkflowRuntime.WorkflowPersistenceService; 1829 if (persistenceSvc != null) 1830 { 1831 persistenceSvc.UnlockWorkflowInstanceState(attemptedRootDispose ? null : this.rootActivity); 1832 if (HasNonEmptyWorkBatch()) 1833 { 1834 this.CommitTransaction(this.rootActivity); 1835 } 1836 } 1837 } 1838 catch (Exception e) 1839 { 1840 if (WorkflowExecutor.IsIrrecoverableException(e)) 1841 { 1842 throw; 1843 } 1844 } 1845 finally 1846 { 1847 // mark instance as invalid 1848 this.IsInstanceValid = false; 1849 DisposeRootActivity(true); 1850 if (currentAtomicActivity != null) 1851 { 1852 atomicActivityEvent.Set(); 1853 atomicActivityEvent.Close(); 1854 } 1855 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Aborted); 1856 } 1857 } 1858 1859 #endregion 1860 1861 #region Suspend 1862 1863 // suspends the schedule instance sync 1864 // must be called only from outside the instance... the thread running the instance must 1865 // never call this method... it should call SuspendOnIdle instead. Suspend(string error)1866 internal bool Suspend(string error) 1867 { 1868 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a suspend request for instance {0}", this.InstanceIdString); 1869 1870 try 1871 { 1872 // check if this is a valid in-memory instance 1873 if (!this.IsInstanceValid) 1874 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1875 1876 1877 // Stop threads from outside - message delivery and control operations 1878 using (this._executorLock.Enter()) 1879 { 1880 // check if this is a valid in-memory instance 1881 if (!this.IsInstanceValid) 1882 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1883 1884 // tell the scheduler to stop running 1885 this.Scheduler.CanRun = false; 1886 1887 // If there were some thread executing the instance, then setting up 1888 // the callback, the thread getting done and the notification coming back 1889 // is racy... so we lock the scheduler 1890 using (new SchedulerLockGuard(this._schedulerLock, this)) 1891 { 1892 using (new ServiceEnvironment(this.rootActivity)) 1893 { 1894 // check if this is a valid in-memory instance 1895 if (!this.IsInstanceValid) 1896 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1897 1898 return this.SuspendOnIdle(error); 1899 } 1900 } 1901 } 1902 } 1903 catch (Exception e) 1904 { 1905 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Suspend attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 1906 throw; 1907 } 1908 } 1909 1910 // this method must be called with the scheduler lock held SuspendOnIdle(string error)1911 internal bool SuspendOnIdle(string error) 1912 { 1913 InstanceLock.AssertIsLocked(this._schedulerLock); 1914 1915 // check if the instance can be suspended 1916 if (!this.IsInstanceValid) 1917 return false; 1918 1919 // if atomic activity in progress, then throw 1920 if (this.currentAtomicActivity != null) 1921 { 1922 this.Scheduler.CanRun = true; 1923 throw new ExecutorLocksHeldException(atomicActivityEvent); 1924 } 1925 else 1926 { 1927 // if already suspended or if just created, then do nothing 1928 WorkflowStatus status = this.WorkflowStatus; 1929 if (status == WorkflowStatus.Suspended || status == WorkflowStatus.Created) 1930 return false; 1931 1932 FireWorkflowSuspending(error); 1933 1934 // tell the scheduler to stop running 1935 this.Scheduler.CanRun = false; 1936 1937 switch (this.rootActivity.ExecutionStatus) 1938 { 1939 case ActivityExecutionStatus.Initialized: 1940 case ActivityExecutionStatus.Executing: 1941 case ActivityExecutionStatus.Canceling: 1942 case ActivityExecutionStatus.Faulting: 1943 case ActivityExecutionStatus.Compensating: 1944 break; 1945 1946 case ActivityExecutionStatus.Closed: 1947 return false; 1948 default: 1949 return false; 1950 } 1951 1952 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Suspending instance {0}", this.InstanceIdString); 1953 1954 // mark it as suspended 1955 this.stateChangedSincePersistence = true; 1956 this.WorkflowStatus = WorkflowStatus.Suspended; 1957 this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, error); 1958 1959 // note: don't persist the instance and don't mark it as invalid. 1960 // The suspended instances must be explicitly unloaded, if required. 1961 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Suspended, error); 1962 return true; 1963 } 1964 } 1965 #endregion 1966 1967 #region Resume 1968 1969 // resumes the schedule instance sync 1970 // must be called only from outside the instance... the thread running the instance must 1971 // never call this method... it should call ResumeOnIdle instead. Resume()1972 internal void Resume() 1973 { 1974 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a resume request for instance {0}", this.InstanceIdString); 1975 1976 try 1977 { 1978 // check if this is a valid in-memory instance 1979 if (!this.IsInstanceValid) 1980 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1981 1982 using (ScheduleWork work = new ScheduleWork(this)) 1983 { 1984 // Stop threads from outside - message delivery and control operations 1985 using (this._executorLock.Enter()) 1986 { 1987 // check if this is a valid in-memory instance 1988 if (!this.IsInstanceValid) 1989 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 1990 1991 if ((this.WorkflowStatus != WorkflowStatus.Suspended)) 1992 return; 1993 1994 using (new SchedulerLockGuard(this._schedulerLock, this)) 1995 { 1996 //@@Undone-- bmalhi there is one test in bat 1997 //which fails here. This check is right thing but im 1998 //commenting it out for bat. 1999 // Microsoft: this fails because when we load an instance into memory it grabs 2000 // the scheduler lock and starts running. By the time the user Resume request 2001 // gets the scheduler lock the instance is often done (the AbortBat test case scenario) 2002 // Balinder is attempting a fix to separate rehydration from resuming execution. 2003 /*if (!this.IsInstanceValid) 2004 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 2005 */ 2006 using (new ServiceEnvironment(this.rootActivity)) 2007 { 2008 this.ResumeOnIdle(true); 2009 } 2010 } 2011 } 2012 } 2013 } 2014 catch (Exception e) 2015 { 2016 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: Resume attempt on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 2017 throw; 2018 } 2019 } 2020 2021 // this method must be called with the scheduler lock held ResumeOnIdle(bool outsideThread)2022 internal bool ResumeOnIdle(bool outsideThread) 2023 { 2024 InstanceLock.AssertIsLocked(this._schedulerLock); 2025 2026 // check if this is a valid in-memory instance 2027 if (!this.IsInstanceValid) 2028 return false; 2029 2030 // if not suspended and CanRun is true, then nothing to resume 2031 if ((this.WorkflowStatus != WorkflowStatus.Suspended) && (!this.Scheduler.CanRun)) 2032 return false; 2033 2034 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Resuming); 2035 2036 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Resuming instance {0}", this.InstanceIdString); 2037 2038 this.stateChangedSincePersistence = true; 2039 this.WorkflowStatus = WorkflowStatus.Running; 2040 this.rootActivity.SetValue(SuspendOrTerminateInfoProperty, string.Empty); 2041 2042 FireEventAfterSchedulerLockDrop(WorkflowEventInternal.Resumed, ThrownException); 2043 2044 using (this._msgDeliveryLock.Enter()) 2045 { 2046 TimerQueue.ResumeDelivery(); 2047 } 2048 2049 // resume the instance 2050 if (outsideThread) 2051 this.Scheduler.Resume(); 2052 else 2053 // being called from within the scheduler thread, so just allow the 2054 // scheduler to run without requesting a new thread 2055 this.Scheduler.CanRun = true; 2056 2057 return true; 2058 } 2059 2060 #endregion 2061 2062 #region Transaction Management 2063 IsActivityInAtomicContext(Activity activity, out Activity atomicActivity)2064 internal bool IsActivityInAtomicContext(Activity activity, out Activity atomicActivity) 2065 { 2066 Debug.Assert(activity != null); 2067 2068 atomicActivity = null; 2069 while (activity != null) 2070 { 2071 if (activity == this.currentAtomicActivity) 2072 { 2073 atomicActivity = activity; 2074 return true; 2075 } 2076 activity = activity.Parent; 2077 } 2078 return false; 2079 } 2080 CreateTransaction(Activity atomicActivity)2081 private void CreateTransaction(Activity atomicActivity) 2082 { 2083 Debug.Assert(this.currentAtomicActivity == null, "There is already a transacted activity running"); 2084 2085 TransactionalProperties transactionalProperties = new TransactionalProperties(); 2086 2087 TransactionOptions tranOpts = new TransactionOptions(); 2088 WorkflowTransactionOptions atomicTxn = TransactedContextFilter.GetTransactionOptions(atomicActivity); 2089 Debug.Assert(atomicTxn != null, "null atomicTxn"); 2090 2091 // 2092 tranOpts.IsolationLevel = atomicTxn.IsolationLevel; 2093 if (tranOpts.IsolationLevel == IsolationLevel.Unspecified) 2094 tranOpts.IsolationLevel = IsolationLevel.Serializable; 2095 2096 tranOpts.Timeout = atomicTxn.TimeoutDuration; 2097 2098 // Create a promotable transaction (can be promoted to DTC when necessary) 2099 // as COM+ user code may want to participate in the transaction 2100 // Enlist to the transaction for abort notification 2101 System.Transactions.CommittableTransaction transaction = new CommittableTransaction(tranOpts); 2102 // Can switch back to using TransactionCompletionHandler once VS562627 is fixed 2103 // transaction.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompletionHandler); 2104 //transaction.EnlistVolatile(new TransactionNotificationEnlistment(this, transaction, atomicActivity), EnlistmentOptions.None); 2105 transactionalProperties.Transaction = transaction; 2106 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2107 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2108 " .Created enlistable transaction " + ((System.Transactions.Transaction)transaction).GetHashCode() + 2109 " with timeout " + tranOpts.Timeout + ", isolation " + tranOpts.IsolationLevel); 2110 2111 // create a local queuing service per atomic context 2112 transactionalProperties.LocalQueuingService = new WorkflowQueuingService(this.qService); 2113 2114 // Store the transaction properties onto the activity 2115 atomicActivity.SetValue(TransactionalPropertiesProperty, transactionalProperties); 2116 2117 // Set current atomic activity 2118 this.currentAtomicActivity = atomicActivity; 2119 atomicActivityEvent = new ManualResetEvent(false); 2120 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + " .Set CurrentAtomicActivity to " + atomicActivity.Name); 2121 } 2122 DisposeTransaction(Activity atomicActivity)2123 private void DisposeTransaction(Activity atomicActivity) 2124 { 2125 // Validates the assumption that only one atomic activity in execution at a time 2126 //Debug.Assert((atomicActivity == this.currentAtomicActivity), 2127 // "Activity context " + atomicActivity.Name + " different from currentAtomicActivity " + this.currentAtomicActivity.Name); 2128 2129 // Cleanup work following a transaction commit or Rollback 2130 TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty); 2131 2132 // release transaction 2133 transactionalProperties.Transaction.Dispose(); 2134 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2135 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2136 " .Disposed enlistable transaction " + 2137 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode()); 2138 2139 // cleanup properties 2140 transactionalProperties.Transaction = null; 2141 transactionalProperties.LocalQueuingService = null; 2142 transactionalProperties.Transaction = null; 2143 2144 // We no longer clear the currentAtomicActivity member here 2145 // but only in the callers of this method (CommitTransaction and RollbackTransaction). 2146 // However, we do this only in CommitTransaction but omit resetting it in RollbackTransaction 2147 // because a complete reversal of a TransactionScopeActivity will restore the 2148 // workflow instance state to a prior checkpointed state. 2149 atomicActivityEvent.Set(); 2150 atomicActivityEvent.Close(); 2151 2152 } 2153 CommitTransaction(Activity activityContext)2154 private void CommitTransaction(Activity activityContext) 2155 { 2156 if (null == Transaction.Current) 2157 { 2158 // 2159 // No TxScopeActivity or external tx 2160 // Ask the TxService to commit 2161 // In this scenario retries are OK as it owns the tx 2162 try 2163 { 2164 // 2165 // Pass a delegate that does the batch commit 2166 // so that it can do retries 2167 this.WorkflowRuntime.TransactionService.CommitWorkBatch(DoResourceManagerCommit); 2168 this.ResourceManager.Complete(); 2169 } 2170 catch 2171 { 2172 this.ResourceManager.HandleFault(); 2173 throw; 2174 } 2175 } 2176 else 2177 { 2178 Debug.Assert(activityContext != null, "null activityContext"); 2179 2180 TransactionalProperties transactionalProperties = null; 2181 bool inTxScope = (activityContext == this.currentAtomicActivity); 2182 // 2183 // Tx is either from TxScopeActivity or it is external 2184 if (inTxScope) 2185 { 2186 transactionalProperties = (TransactionalProperties)activityContext.GetValue(TransactionalPropertiesProperty); 2187 if (CheckAndProcessTransactionAborted(transactionalProperties)) 2188 return; 2189 } 2190 // 2191 // Commit the batches and rely on the enlistment to do completion/rollback work for the batches 2192 // TxService must use the ambient transaction directly or do a dependent clone. 2193 try 2194 { 2195 this.WorkflowRuntime.TransactionService.CommitWorkBatch(DoResourceManagerCommit); 2196 } 2197 catch 2198 { 2199 // 2200 // This tx is doomed, clean up batches 2201 ResourceManager.HandleFault(); 2202 throw; 2203 } 2204 finally 2205 { 2206 if (inTxScope) 2207 { 2208 // DTC transaction commit needs to be done after TransactionScope Complete 2209 // because the Commit Voting needs to happen on the the original thread 2210 // that created the transaction. Otherwise the transaction will abort after timing out. 2211 Debug.Assert(null != transactionalProperties, "TransactionProperties from TransactionScopeActivity should not be null."); 2212 DisposeTransactionScope(transactionalProperties); 2213 } 2214 } 2215 // 2216 // If we are in a tx scope we need to commit our tx 2217 if (inTxScope) 2218 { 2219 // 2220 // The tx will be Committable if there was not ambient tx when the scope started 2221 // It will be Dependent if there was an ambient tx when the scope started 2222 // (The external case is explicitly disabled for V1) 2223 try 2224 { 2225 CommittableTransaction ctx = transactionalProperties.Transaction as CommittableTransaction; 2226 if (null != ctx) 2227 { 2228 try 2229 { 2230 ctx.Commit(); 2231 } 2232 catch 2233 { 2234 qService.PostPersist(false); 2235 throw; 2236 } 2237 2238 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2239 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2240 " .Committed CommittableTransaction " + 2241 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode()); 2242 } 2243 2244 DependentTransaction dtx = transactionalProperties.Transaction as DependentTransaction; 2245 if (null != dtx) 2246 { 2247 try 2248 { 2249 dtx.Complete(); 2250 } 2251 catch 2252 { 2253 qService.PostPersist(false); 2254 throw; 2255 } 2256 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2257 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2258 " .Completed DependentTransaction " + 2259 ((System.Transactions.Transaction)transactionalProperties.Transaction).GetHashCode()); 2260 } 2261 } 2262 catch 2263 { 2264 // 2265 // This tx (scope activity or external) is doomed, clean up batches 2266 ResourceManager.HandleFault(); 2267 throw; 2268 } 2269 2270 // 2271 // If commit throws we'll do this call in RollbackTransaction. 2272 // However, the currentAtomicActivity member is not reset in RollbackTransaction 2273 // because a complete reversal of a TransactionScopeActivity will restore the 2274 // workflow instance state to a prior checkpointed state. 2275 DisposeTransaction(activityContext); 2276 this.currentAtomicActivity = null; 2277 2278 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2279 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2280 "Reset CurrentAtomicActivity to null"); 2281 2282 } 2283 // 2284 // Tell the batches that we committed successfully 2285 ResourceManager.Complete(); 2286 } 2287 } 2288 /// <summary> 2289 /// Call commit on the VolatileResourceManager to commit all work in the batch. 2290 /// Transaction.Current must be non-null. 2291 /// </summary> DoResourceManagerCommit()2292 private void DoResourceManagerCommit() 2293 { 2294 if (null == Transaction.Current) 2295 throw new Exception(ExecutionStringManager.NullAmbientTransaction); 2296 2297 this.ResourceManager.Commit(); 2298 } 2299 RollbackTransaction(Exception exp, Activity activityContext)2300 private void RollbackTransaction(Exception exp, Activity activityContext) 2301 { 2302 if (activityContext == this.currentAtomicActivity) 2303 { 2304 Debug.Assert((activityContext == this.currentAtomicActivity), 2305 "Activity context " + activityContext.Name + " different from currentAtomicActivity " + this.currentAtomicActivity.Name); 2306 2307 TransactionalProperties transactionalProperties = (TransactionalProperties)activityContext.GetValue(TransactionalPropertiesProperty); 2308 if (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed) 2309 { 2310 // If TransactionState is not already AbortProcessed, Set it to AbortProcessed as we have raised exception for it already 2311 // Possible call paths for which it's not already AbortProcessed: 2312 // TransactionState == Aborted if due to transaction failure notified through TransactionCompletionHandler 2313 // TransactionState == Ok if Called from external exception raising (e.g. a throw activity in Atomic context) 2314 transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed; 2315 } 2316 2317 Debug.Assert((transactionalProperties.Transaction != null), "Null Transaction while transaction is present"); 2318 Debug.Assert((transactionalProperties.LocalQueuingService != null), "Null LocalQueuingService while transaction is present"); 2319 2320 try 2321 { 2322 DisposeTransactionScope(transactionalProperties); 2323 2324 // roll back transaction 2325 System.Transactions.Transaction transaction = transactionalProperties.Transaction; 2326 if (System.Transactions.TransactionStatus.Aborted != transaction.TransactionInformation.Status) 2327 transaction.Rollback(); 2328 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2329 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2330 " .Aborted enlistable transaction " + 2331 ((System.Transactions.Transaction)transaction).GetHashCode()); 2332 } 2333 finally 2334 { 2335 // roolback queuing service state 2336 WorkflowQueuingService queuingService = transactionalProperties.LocalQueuingService; 2337 queuingService.Complete(false); 2338 2339 // dispose transaction. However, do not reset the currentAtomicActivity member here 2340 // because a complete reversal of a TransactionScopeActivity will restore the 2341 // workflow instance state to a prior checkpointed state. 2342 DisposeTransaction(this.currentAtomicActivity); 2343 } 2344 } 2345 } 2346 2347 #region VolatileEnlistment for Transaction Completion Notification 2348 /* 2349 * Leaving this class in place as we will need it for the flow through tx story in V2 2350 class TransactionNotificationEnlistment : IEnlistmentNotification, IActivityEventListener<EventArgs> 2351 { 2352 WorkflowExecutor workflowExecutor; 2353 Transaction transaction; 2354 Activity atomicActivity; 2355 internal TransactionNotificationEnlistment(WorkflowExecutor exec, Transaction tx, Activity atomicActivity) 2356 { 2357 this.workflowExecutor = exec; 2358 this.transaction = tx; 2359 this.atomicActivity = atomicActivity; 2360 } 2361 2362 #region IEnlistmentNotification Members 2363 2364 void IEnlistmentNotification.Commit(Enlistment enlistment) 2365 { 2366 enlistment.Done(); 2367 } 2368 2369 void IEnlistmentNotification.InDoubt(Enlistment enlistment) 2370 { 2371 enlistment.Done(); 2372 } 2373 2374 void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment) 2375 { 2376 preparingEnlistment.Prepared(); 2377 } 2378 2379 void IEnlistmentNotification.Rollback(Enlistment enlistment) 2380 { 2381 // 2382 // Currently this method isn't used. 2383 // The problem is that we must acquire the sched lock in order to schedule 2384 // an item. While we wait trying to acquire the lock the transaction is held open. 2385 // If the instance is idle we acquire the lock right away and this works fine. 2386 // However is we have items to run we'll check the transaction, find that it is aborted 2387 // and start exception handling. During the entire exception handling process the transaction 2388 // and the associated connections will be held open. This is not good. 2389 // Post V1 we need scheduler changes to allow us to safely asynchronously schedule work 2390 // without taking the scheduler lock. 2391 enlistment.Done(); 2392 // 2393 // ensure transaction timeout/abort is processed in case of a 2394 // blocked activity inside a transactional scope 2395 ScheduleTransactionTimeout(); 2396 } 2397 2398 private void ScheduleTransactionTimeout() 2399 { 2400 try 2401 { 2402 // 2403 // We're going to check executor state and possibly enqueue a workitem 2404 // Must take the scheduleExecutor lock 2405 using (this.workflowExecutor._schedulerLock.Enter()) 2406 { 2407 if (!this.workflowExecutor.IsInstanceValid) 2408 return; 2409 2410 // If the exception has already been taken care of, ignore this abort notification 2411 Activity curAtomicActivity = this.workflowExecutor.currentAtomicActivity; 2412 if ((curAtomicActivity != null)&&(curAtomicActivity==atomicActivity)) 2413 { 2414 TransactionalProperties transactionalProperties = (TransactionalProperties)curAtomicActivity.GetValue(TransactionalPropertiesProperty); 2415 if ((transactionalProperties.Transaction == this.transaction) && 2416 (transactionalProperties.TransactionState != TransactionProcessState.AbortProcessed)) 2417 { 2418 transactionalProperties.TransactionState = TransactionProcessState.Aborted; 2419 2420 using (this.workflowExecutor.MessageDeliveryLock.Enter()) 2421 { 2422 using (new ServiceEnvironment(this.workflowExecutor.RootActivity)) 2423 { 2424 using (this.workflowExecutor.SetCurrentActivity(curAtomicActivity)) 2425 { 2426 // 2427 // This will schedule (async) a work item to cancel the tx scope activity 2428 // However this item will never get run - we always check if the 2429 // tx has aborted prior to running any items so this is really 2430 // just a "wake up" notification to the scheduler. 2431 Activity contextActivity = ContextActivityUtils.ContextActivity(curAtomicActivity); 2432 ActivityExecutorDelegateInfo<EventArgs> dummyCallback = new ActivityExecutorDelegateInfo<EventArgs>(this, contextActivity, true); 2433 dummyCallback.InvokeDelegate(contextActivity, EventArgs.Empty, false); 2434 } 2435 } 2436 } 2437 } 2438 } 2439 } 2440 } 2441 catch (Exception e) 2442 { 2443 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "AbortNotificationEnlistment: instanceId: {0} failed to process ScheduleTransactionTimeout with exception {1} ", this.workflowExecutor.this.InstanceIdString, e.Message); 2444 } 2445 } 2446 2447 void IActivityEventListener<EventArgs>.OnEvent(object sender, EventArgs e) 2448 { 2449 // this will never be invoked since Scheduler will process the transaction aborted request 2450 } 2451 2452 #endregion 2453 }*/ 2454 #endregion VolatileEnlistment for AbortNotification 2455 CheckAndProcessTransactionAborted(TransactionalProperties transactionalProperties)2456 internal static bool CheckAndProcessTransactionAborted(TransactionalProperties transactionalProperties) 2457 { 2458 if (transactionalProperties.Transaction != null && transactionalProperties.Transaction.TransactionInformation.Status != TransactionStatus.Aborted) 2459 return false; 2460 2461 // If transaction aborted but not processed, 2462 // process it (i.e. throw to invoke Exception handling) 2463 // otherwise return if transaction aborted 2464 switch (transactionalProperties.TransactionState) 2465 { 2466 case TransactionProcessState.Ok: 2467 case TransactionProcessState.Aborted: 2468 transactionalProperties.TransactionState = TransactionProcessState.AbortProcessed; 2469 throw new TransactionAbortedException(); 2470 2471 case TransactionProcessState.AbortProcessed: 2472 return true; 2473 2474 default: 2475 return false; 2476 } 2477 } 2478 DisposeTransactionScope(TransactionalProperties transactionalProperties)2479 private void DisposeTransactionScope(TransactionalProperties transactionalProperties) 2480 { 2481 if (transactionalProperties.TransactionScope != null) 2482 { 2483 // Need to call Complete othwise the transaction will be aborted 2484 transactionalProperties.TransactionScope.Complete(); 2485 transactionalProperties.TransactionScope.Dispose(); 2486 transactionalProperties.TransactionScope = null; 2487 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2488 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2489 "Left TransactionScope, Current atomic acitivity was " + 2490 ((this.currentAtomicActivity == null) ? null : this.currentAtomicActivity.Name)); 2491 } 2492 } 2493 2494 #region delay scheduling of items for ACID purposes 2495 AddItemToBeScheduledLater(Activity atomicActivity, SchedulableItem item)2496 private void AddItemToBeScheduledLater(Activity atomicActivity, SchedulableItem item) 2497 { 2498 if (atomicActivity == null) 2499 return; 2500 2501 // Activity may not be atomic and is an activity which is not 2502 // yet scheduled for execution (typically receive case) 2503 if (!atomicActivity.SupportsTransaction) 2504 return; 2505 2506 TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty); 2507 if (transactionalProperties != null) 2508 { 2509 lock (transactionalProperties) 2510 { 2511 List<SchedulableItem> notifications = null; 2512 notifications = transactionalProperties.ItemsToBeScheduledAtCompletion; 2513 if (notifications == null) 2514 { 2515 notifications = new List<SchedulableItem>(); 2516 transactionalProperties.ItemsToBeScheduledAtCompletion = notifications; 2517 } 2518 notifications.Add(item); 2519 } 2520 } 2521 } 2522 ScheduleDelayedItems(Activity atomicActivity)2523 private void ScheduleDelayedItems(Activity atomicActivity) 2524 { 2525 List<SchedulableItem> items = null; 2526 TransactionalProperties transactionalProperties = (TransactionalProperties)atomicActivity.GetValue(TransactionalPropertiesProperty); 2527 2528 if (transactionalProperties == null) 2529 return; 2530 2531 lock (transactionalProperties) 2532 { 2533 items = transactionalProperties.ItemsToBeScheduledAtCompletion; 2534 if (items == null) 2535 return; 2536 2537 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, 2538 "Workflow Runtime: WorkflowExecutor: instanceId: " + this.InstanceIdString + 2539 " .Scheduling delayed " + items.Count + " number of items"); 2540 2541 foreach (SchedulableItem item in items) 2542 { 2543 this.Scheduler.ScheduleItem(item, false, true); 2544 } 2545 items.Clear(); 2546 2547 transactionalProperties.ItemsToBeScheduledAtCompletion = null; 2548 } 2549 } 2550 2551 #endregion delay scheduling of items for ACID purposes 2552 2553 #endregion Transaction Management 2554 2555 #region Exception Management 2556 ExceptionOccured(Exception exp, Activity currentActivity, string originalActivityId)2557 internal void ExceptionOccured(Exception exp, Activity currentActivity, string originalActivityId) 2558 { 2559 Debug.Assert(exp != null, "null exp"); 2560 Debug.Assert(currentActivity != null, "null currentActivity"); 2561 // exception tracking work 2562 // 2563 if (this.ThrownException != exp) 2564 { 2565 // first time exception 2566 this.ThrownException = exp; 2567 this.activityThrowingException = currentActivity.QualifiedName; 2568 originalActivityId = currentActivity.QualifiedName; 2569 } 2570 else 2571 { 2572 // rethrown exception 2573 originalActivityId = this.activityThrowingException; 2574 } 2575 Guid contextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(currentActivity).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid; 2576 Guid parentContextGuid = Guid.Empty; 2577 if (null != currentActivity.Parent) 2578 parentContextGuid = ((ActivityExecutionContextInfo)ContextActivityUtils.ContextActivity(currentActivity.Parent).GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid; 2579 this.FireExceptionOccured(exp, currentActivity.QualifiedName, originalActivityId, contextGuid, parentContextGuid); 2580 2581 // notify the activity. 2582 // 2583 using (new ServiceEnvironment(currentActivity)) 2584 { 2585 using (SetCurrentActivity(currentActivity)) 2586 { 2587 using (ActivityExecutionContext executionContext = new ActivityExecutionContext(currentActivity, true)) 2588 executionContext.FaultActivity(exp); 2589 } 2590 } 2591 2592 // transaction and batching clean-up on the activity that handles the exception 2593 this.RollbackTransaction(exp, currentActivity); 2594 if ((currentActivity is TransactionScopeActivity) || (exp is PersistenceException)) 2595 this.BatchCollection.RollbackBatch(currentActivity); 2596 } 2597 2598 internal Exception ThrownException 2599 { 2600 get { return thrownException; } 2601 set { thrownException = value; } 2602 } 2603 IsIrrecoverableException(Exception e)2604 internal static bool IsIrrecoverableException(Exception e) 2605 { 2606 return ((e is OutOfMemoryException) || 2607 (e is StackOverflowException) || 2608 (e is ThreadInterruptedException) || 2609 (e is ThreadAbortException)); 2610 } 2611 2612 #endregion Exception Management 2613 2614 #region Tracking Management 2615 Track(Activity activity, string key, object args)2616 internal void Track(Activity activity, string key, object args) 2617 { 2618 FireUserTrackPoint(activity, key, args); 2619 } 2620 FireExceptionOccured(Exception e, string currentActivityPath, string originalActivityPath, Guid contextGuid, Guid parentContextGuid)2621 internal void FireExceptionOccured(Exception e, string currentActivityPath, string originalActivityPath, Guid contextGuid, Guid parentContextGuid) 2622 { 2623 FireWorkflowException(e, currentActivityPath, originalActivityPath, contextGuid, parentContextGuid); 2624 } 2625 2626 #endregion 2627 2628 #region Dynamic Update Management 2629 2630 #region Dynamic Update From Outside the instance GetWorkflowDefinition(string workflowContext)2631 internal Activity GetWorkflowDefinition(string workflowContext) 2632 { 2633 if (workflowContext == null) 2634 throw new ArgumentNullException("workflowContext"); 2635 2636 return this.WorkflowDefinition; 2637 } 2638 GetWorkflowDefinitionClone(string workflowContext)2639 internal Activity GetWorkflowDefinitionClone(string workflowContext) 2640 { 2641 if (workflowContext == null) 2642 throw new ArgumentNullException("workflowContext"); 2643 2644 Activity definition = this.WorkflowDefinition; 2645 2646 using (new WorkflowDefinitionLock(definition)) 2647 { 2648 return definition.Clone(); 2649 } 2650 } 2651 ApplyWorkflowChanges(WorkflowChanges workflowChanges)2652 internal void ApplyWorkflowChanges(WorkflowChanges workflowChanges) 2653 { 2654 // Accessing InstanceId is not thread safe here! 2655 //WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a dynamic update request from outside for instance {0}", this.InstanceIdString); 2656 DiagnosticStackTrace("dynamic update request"); 2657 2658 // check arguments 2659 if (workflowChanges == null) 2660 throw new ArgumentNullException("workflowChanges"); 2661 2662 // check if this is a valid in-memory instance 2663 if (!this.IsInstanceValid) 2664 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 2665 2666 if (this.currentAtomicActivity != null) 2667 throw new InvalidOperationException(ExecutionStringManager.Error_InsideAtomicScope); 2668 2669 try 2670 { 2671 using (ScheduleWork work = new ScheduleWork(this)) 2672 { 2673 // block other instance operations from outside 2674 using (this._executorLock.Enter()) 2675 { 2676 // check if this is a valid in-memory instance 2677 if (!this.IsInstanceValid) 2678 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 2679 2680 // get the instance to stop running 2681 this.Scheduler.CanRun = false; 2682 using (new SchedulerLockGuard(this._schedulerLock, this)) 2683 { 2684 using (new ServiceEnvironment(this.rootActivity)) 2685 { 2686 bool localSuspend = false; 2687 2688 // check if this is a valid in-memory instance 2689 if (!this.IsInstanceValid) 2690 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 2691 2692 try 2693 { 2694 // check the status of the schedule 2695 switch (this.WorkflowStatus) 2696 { 2697 ////case ActivityExecutionStatus.Completed: 2698 // 2699 case WorkflowStatus.Completed: 2700 case WorkflowStatus.Terminated: 2701 throw new InvalidOperationException( 2702 ExecutionStringManager.InvalidOperationRequest); 2703 case WorkflowStatus.Suspended: 2704 // instance already suspended 2705 localSuspend = false; 2706 break; 2707 default: 2708 // suspend the instance 2709 this.SuspendOnIdle(null); 2710 localSuspend = true; 2711 break; 2712 } 2713 2714 // apply the changes 2715 workflowChanges.ApplyTo(this.rootActivity); 2716 } 2717 finally 2718 { 2719 if (localSuspend) 2720 { 2721 // @undone: for now this will not return till the instance is done 2722 // Once Kumar has fixed 4335, we can enable this. 2723 this.ResumeOnIdle(true); 2724 } 2725 } 2726 } 2727 } // release lock on scheduler 2728 } 2729 } 2730 } 2731 catch (Exception e) 2732 { 2733 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "Workflow Runtime: WorkflowExecutor: dynamic update attempt from outside on instance '{0}' threw an exception '{1}' at {2}", this.InstanceIdString, e.Message, e.StackTrace); 2734 throw; 2735 } 2736 } 2737 #endregion Dynamic Update From Outside the instance OnBeforeDynamicChange(IList<WorkflowChangeAction> changes)2738 internal bool OnBeforeDynamicChange(IList<WorkflowChangeAction> changes) 2739 { 2740 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Got a dynamic update request for instance {0}", this.InstanceIdString); 2741 2742 if (!this.IsInstanceValid) 2743 throw new InvalidOperationException(ExecutionStringManager.WorkflowNotValid); 2744 2745 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Found a match for the schedule in updating instance {0}", this.InstanceIdString); 2746 2747 FireDynamicUpdateBegin(changes); 2748 2749 return true; 2750 } 2751 OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes)2752 internal void OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes) 2753 { 2754 if (updateSucceeded) 2755 { 2756 RefreshWorkflowDefinition(); 2757 //Commit temporary work 2758 FireDynamicUpdateCommit(changes); 2759 FireWorkflowExecutionEvent(this, WorkflowEventInternal.Changed); 2760 } 2761 else 2762 { 2763 // Rollback 2764 FireDynamicUpdateRollback(changes); 2765 } 2766 2767 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: Done updating a schedule in instance {0}", this.InstanceIdString); 2768 2769 } 2770 2771 bool IWorkflowCoreRuntime.IsDynamicallyUpdated 2772 { 2773 get 2774 { 2775 return ((Activity)this.WorkflowDefinition).GetValue(WorkflowChanges.WorkflowChangeActionsProperty) != null; 2776 } 2777 } 2778 #endregion 2779 2780 #region Diagnostic tracing 2781 2782 [System.Diagnostics.Conditional("DEBUG")] DiagnosticStackTrace(string reason)2783 void DiagnosticStackTrace(string reason) 2784 { 2785 StackTrace st = new StackTrace(true); 2786 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Information, 0, "Workflow Runtime: WorkflowExecutor: InstanceId: {0} : {1} stack trace: {2}", this.InstanceIdString, reason, st.ToString()); 2787 } 2788 2789 #endregion 2790 2791 #region Timer event support 2792 2793 WaitCallback IWorkflowCoreRuntime.ProcessTimersCallback 2794 { 2795 get 2796 { 2797 return new WaitCallback(this.WorkflowInstance.ProcessTimers); 2798 } 2799 } 2800 2801 #endregion 2802 2803 #region IServiceProvider members 2804 IServiceProvider.GetService(Type serviceType)2805 object IServiceProvider.GetService(Type serviceType) 2806 { 2807 return ((IWorkflowCoreRuntime)this).GetService(this.rootActivity, serviceType); 2808 } 2809 #endregion 2810 2811 #region IWorkflowCoreRuntime Members 2812 2813 Activity IWorkflowCoreRuntime.CurrentActivity 2814 { 2815 get 2816 { 2817 #pragma warning disable 56503 2818 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2819 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2820 #pragma warning restore 56503 2821 return this.CurrentActivity; 2822 } 2823 } 2824 Activity IWorkflowCoreRuntime.CurrentAtomicActivity 2825 { 2826 get 2827 { 2828 return this.currentAtomicActivity; 2829 } 2830 } IWorkflowCoreRuntime.StartWorkflow(Type workflowType, Dictionary<string, object> namedArgumentValues)2831 Guid IWorkflowCoreRuntime.StartWorkflow(Type workflowType, Dictionary<string, object> namedArgumentValues) 2832 { 2833 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2834 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2835 2836 Guid instanceId = Guid.Empty; 2837 WorkflowInstance instance = this.WorkflowRuntime.InternalCreateWorkflow(new CreationContext(workflowType, this, this.CurrentActivity.QualifiedName, namedArgumentValues), Guid.NewGuid()); 2838 if (instance != null) 2839 { 2840 instanceId = instance.InstanceId; 2841 instance.Start(); 2842 } 2843 2844 return instanceId; 2845 } IWorkflowCoreRuntime.ScheduleItem(SchedulableItem item, bool isInAtomicTransaction, bool transacted, bool queueInTransaction)2846 void IWorkflowCoreRuntime.ScheduleItem(SchedulableItem item, bool isInAtomicTransaction, bool transacted, bool queueInTransaction) 2847 { 2848 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2849 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2850 if (!queueInTransaction) 2851 this.Scheduler.ScheduleItem(item, isInAtomicTransaction, transacted); 2852 else 2853 this.AddItemToBeScheduledLater(this.CurrentActivity, item); 2854 } SetCurrentActivity(Activity activity)2855 public IDisposable SetCurrentActivity(Activity activity) 2856 { 2857 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2858 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2859 Activity oldCurrentActivity = this.CurrentActivity; 2860 this.CurrentActivity = activity; 2861 return new ResetCurrentActivity(this, oldCurrentActivity); 2862 } 2863 Guid IWorkflowCoreRuntime.InstanceID 2864 { 2865 get 2866 { 2867 #pragma warning disable 56503 2868 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2869 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2870 #pragma warning restore 56503 2871 return this.InstanceId; 2872 } 2873 } IWorkflowCoreRuntime.SuspendInstance(string suspendDescription)2874 bool IWorkflowCoreRuntime.SuspendInstance(string suspendDescription) 2875 { 2876 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2877 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2878 return this.SuspendOnIdle(suspendDescription); 2879 } IWorkflowCoreRuntime.TerminateInstance(Exception e)2880 void IWorkflowCoreRuntime.TerminateInstance(Exception e) 2881 { 2882 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2883 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2884 2885 this.ThrownException = e; 2886 this.TerminateOnIdle(WorkflowExecutor.GetNestedExceptionMessage(e)); 2887 } IWorkflowCoreRuntime.Resume()2888 bool IWorkflowCoreRuntime.Resume() 2889 { 2890 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2891 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2892 return this.ResumeOnIdle(false); 2893 } IWorkflowCoreRuntime.RaiseHandlerInvoking(Delegate handlerDelegate)2894 void IWorkflowCoreRuntime.RaiseHandlerInvoking(Delegate handlerDelegate) 2895 { 2896 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2897 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2898 FireWorkflowHandlerInvokingEvent(this, WorkflowEventInternal.HandlerInvoking, handlerDelegate); 2899 } IWorkflowCoreRuntime.RaiseActivityExecuting(Activity activity)2900 void IWorkflowCoreRuntime.RaiseActivityExecuting(Activity activity) 2901 { 2902 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2903 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2904 FireActivityExecuting(this, activity); 2905 } IWorkflowCoreRuntime.RaiseHandlerInvoked()2906 void IWorkflowCoreRuntime.RaiseHandlerInvoked() 2907 { 2908 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2909 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2910 FireWorkflowExecutionEvent(this, WorkflowEventInternal.HandlerInvoked); 2911 } IWorkflowCoreRuntime.CheckpointInstanceState(Activity currentActivity)2912 void IWorkflowCoreRuntime.CheckpointInstanceState(Activity currentActivity) 2913 { 2914 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2915 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2916 2917 // Call CheckpointInstanceState() before CreateTransaction() because 2918 // creating a TX can fail and then we end up ----ing up in HandleFault(). 2919 using (MessageDeliveryLock.Enter()) 2920 { 2921 this.WorkflowStateRollbackService.CheckpointInstanceState(); 2922 } 2923 this.CreateTransaction(currentActivity); 2924 } IWorkflowCoreRuntime.RequestRevertToCheckpointState(Activity currentActivity, EventHandler<EventArgs> callbackHandler, EventArgs callbackData, bool suspendOnRevert, string suspendInfo)2925 void IWorkflowCoreRuntime.RequestRevertToCheckpointState(Activity currentActivity, EventHandler<EventArgs> callbackHandler, EventArgs callbackData, bool suspendOnRevert, string suspendInfo) 2926 { 2927 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2928 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2929 this.WorkflowStateRollbackService.RequestRevertToCheckpointState(currentActivity, callbackHandler, callbackData, suspendOnRevert, suspendInfo); 2930 } IWorkflowCoreRuntime.DisposeCheckpointState()2931 void IWorkflowCoreRuntime.DisposeCheckpointState() 2932 { 2933 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2934 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2935 this.WorkflowStateRollbackService.DisposeCheckpointState(); 2936 } IWorkflowCoreRuntime.GetNewContextActivityId()2937 int IWorkflowCoreRuntime.GetNewContextActivityId() 2938 { 2939 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2940 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2941 return this.GetNewContextId(); 2942 } IWorkflowCoreRuntime.GetContextActivityForId(int stateId)2943 Activity IWorkflowCoreRuntime.GetContextActivityForId(int stateId) 2944 { 2945 if (this.subStateMap.ContainsKey(stateId)) 2946 return this.subStateMap[stateId]; 2947 return null; 2948 } IWorkflowCoreRuntime.RaiseException(Exception e, Activity activity, string responsibleActivity)2949 void IWorkflowCoreRuntime.RaiseException(Exception e, Activity activity, string responsibleActivity) 2950 { 2951 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2952 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2953 this.ExceptionOccured(e, activity, responsibleActivity); 2954 } IWorkflowCoreRuntime.RegisterContextActivity(Activity activity)2955 void IWorkflowCoreRuntime.RegisterContextActivity(Activity activity) 2956 { 2957 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2958 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2959 this.RegisterDynamicActivity(activity, false); 2960 } IWorkflowCoreRuntime.UnregisterContextActivity(Activity activity)2961 void IWorkflowCoreRuntime.UnregisterContextActivity(Activity activity) 2962 { 2963 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2964 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2965 this.UnregisterDynamicActivity(activity); 2966 } IWorkflowCoreRuntime.ActivityStatusChanged(Activity activity, bool transacted, bool committed)2967 void IWorkflowCoreRuntime.ActivityStatusChanged(Activity activity, bool transacted, bool committed) 2968 { 2969 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 2970 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 2971 if (!committed) 2972 { 2973 if (activity.ExecutionStatus == ActivityExecutionStatus.Executing) 2974 { 2975 bool mustPersistState = (TransactedContextFilter.GetTransactionOptions(activity) != null) ? true : false; 2976 if (mustPersistState && this.WorkflowRuntime.WorkflowPersistenceService == null) 2977 { 2978 string errMsg = String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceService, this.InstanceId); 2979 WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, errMsg); 2980 throw new InvalidOperationException(errMsg); 2981 } 2982 } 2983 else if (activity.ExecutionStatus == ActivityExecutionStatus.Closed) 2984 { 2985 this.ScheduleDelayedItems(activity); 2986 } 2987 else if (activity.ExecutionStatus == ActivityExecutionStatus.Canceling || activity.ExecutionStatus == ActivityExecutionStatus.Faulting) 2988 { 2989 if (TransactedContextFilter.GetTransactionOptions(activity) != null) 2990 { 2991 // If the activity is transactional and is being canceled, roll back 2992 // any batches associated with it. (This does nothing if the activity 2993 // had no batch.) 2994 this.BatchCollection.RollbackBatch(activity); 2995 } 2996 } 2997 } 2998 2999 if (!committed) 3000 { 3001 FireActivityStatusChange(this, activity); 3002 } 3003 3004 if (activity.ExecutionStatus == ActivityExecutionStatus.Closed) 3005 { 3006 if (!(activity is ICompensatableActivity) || ((activity is ICompensatableActivity) && activity.CanUninitializeNow)) 3007 CorrelationTokenCollection.UninitializeCorrelationTokens(activity); 3008 } 3009 } 3010 IWorkflowCoreRuntime.PersistInstanceState(Activity activity)3011 void IWorkflowCoreRuntime.PersistInstanceState(Activity activity) 3012 { 3013 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 3014 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 3015 3016 bool persistOnClose = false; 3017 if (activity.UserData.Contains(typeof(PersistOnCloseAttribute))) 3018 { 3019 persistOnClose = (bool)activity.UserData[typeof(PersistOnCloseAttribute)]; 3020 } 3021 else 3022 { 3023 object[] attributes = activity.GetType().GetCustomAttributes(typeof(PersistOnCloseAttribute), true); 3024 if (attributes != null && attributes.Length > 0) 3025 persistOnClose = true; 3026 } 3027 if (persistOnClose && this.WorkflowRuntime.GetService<WorkflowPersistenceService>() == null) 3028 throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.MissingPersistenceServiceWithPersistOnClose, activity.Name)); 3029 3030 this.ScheduleDelayedItems(activity); 3031 3032 bool unlock = (activity.Parent == null) ? true : false; 3033 bool needsCompensation = false; // 3034 this.Persist(activity, unlock, needsCompensation); 3035 } 3036 IWorkflowCoreRuntime.LoadContextActivity(ActivityExecutionContextInfo contextInfo, Activity outerActivity)3037 Activity IWorkflowCoreRuntime.LoadContextActivity(ActivityExecutionContextInfo contextInfo, Activity outerActivity) 3038 { 3039 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 3040 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 3041 Activity contextActivity = null; 3042 if (this.completedContextActivities.Contains(contextInfo)) 3043 { 3044 contextActivity = (Activity)this.completedContextActivities[contextInfo]; 3045 this.completedContextActivities.Remove(contextInfo); 3046 3047 if (contextActivity.Parent != outerActivity.Parent) 3048 contextActivity.parent = outerActivity.Parent; 3049 } 3050 else 3051 { 3052 using (RuntimeEnvironment runtimeEnv = new RuntimeEnvironment(this.WorkflowRuntime)) 3053 { 3054 contextActivity = this.WorkflowRuntime.WorkflowPersistenceService.LoadCompletedContextActivity(contextInfo.ContextGuid, outerActivity); 3055 if (contextActivity == null) 3056 throw new InvalidOperationException(ExecutionStringManager.LoadContextActivityFailed); 3057 } 3058 } 3059 return contextActivity; 3060 } IWorkflowCoreRuntime.SaveContextActivity(Activity contextActivity)3061 void IWorkflowCoreRuntime.SaveContextActivity(Activity contextActivity) 3062 { 3063 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 3064 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 3065 this.completedContextActivities.Add((ActivityExecutionContextInfo)contextActivity.GetValue(Activity.ActivityExecutionContextInfoProperty), contextActivity); 3066 } 3067 Activity IWorkflowCoreRuntime.RootActivity 3068 { 3069 get 3070 { 3071 return this.rootActivity; 3072 } 3073 } IWorkflowCoreRuntime.GetService(Activity activity, Type serviceType)3074 object IWorkflowCoreRuntime.GetService(Activity activity, Type serviceType) 3075 { 3076 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 3077 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 3078 3079 if (serviceType == typeof(IWorkflowCoreRuntime)) 3080 { 3081 return this; 3082 } 3083 else if (serviceType == typeof(WorkflowRuntime))//sorry, no. 3084 return null; 3085 else if (serviceType == typeof(WorkflowQueuingService)) 3086 { 3087 WorkflowQueuingService queuingService = ServiceEnvironment.QueuingService; 3088 if (queuingService == null) 3089 queuingService = this.qService; // root Q service 3090 3091 queuingService.CallingActivity = ContextActivityUtils.ContextActivity(activity); 3092 return queuingService; 3093 } 3094 else if (serviceType == typeof(IWorkflowDebuggerService)) 3095 { 3096 return this._workflowDebuggerService as IWorkflowDebuggerService; 3097 } 3098 3099 return this.WorkflowRuntime.GetService(serviceType); 3100 } IWorkflowCoreRuntime.OnBeforeDynamicChange(IList<WorkflowChangeAction> changes)3101 bool IWorkflowCoreRuntime.OnBeforeDynamicChange(IList<WorkflowChangeAction> changes) 3102 { 3103 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 3104 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 3105 return this.OnBeforeDynamicChange(changes); 3106 } IWorkflowCoreRuntime.OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes)3107 void IWorkflowCoreRuntime.OnAfterDynamicChange(bool updateSucceeded, IList<WorkflowChangeAction> changes) 3108 { 3109 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 3110 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 3111 this.OnAfterDynamicChange(updateSucceeded, changes); 3112 } IWorkflowCoreRuntime.Track(string key, object args)3113 void IWorkflowCoreRuntime.Track(string key, object args) 3114 { 3115 if (!ServiceEnvironment.IsInServiceThread(this.InstanceId)) 3116 throw new InvalidOperationException(ExecutionStringManager.MustUseRuntimeThread); 3117 this.Track(this.CurrentActivity, key, args); 3118 } 3119 #endregion 3120 3121 #region ResetCurrentActivity Class 3122 3123 private class ResetCurrentActivity : IDisposable 3124 { 3125 private WorkflowExecutor workflowExecutor = null; 3126 private Activity oldCurrentActivity = null; ResetCurrentActivity(WorkflowExecutor workflowExecutor, Activity oldCurrentActivity)3127 internal ResetCurrentActivity(WorkflowExecutor workflowExecutor, Activity oldCurrentActivity) 3128 { 3129 this.workflowExecutor = workflowExecutor; 3130 this.oldCurrentActivity = oldCurrentActivity; 3131 } IDisposable.Dispose()3132 void IDisposable.Dispose() 3133 { 3134 this.workflowExecutor.CurrentActivity = oldCurrentActivity; 3135 } 3136 } 3137 #endregion 3138 3139 // GetTransientBatch is defined in this class but if the workflow is running under a V2.0 Interop environment, 3140 // it calls the Interop activity to get the Batch collection. GetTransientBatch(DependencyObject dependencyObject)3141 private static object GetTransientBatch(DependencyObject dependencyObject) 3142 { 3143 if (dependencyObject == null) 3144 throw new ArgumentNullException("dependencyObject"); 3145 if (!(dependencyObject is Activity)) 3146 throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidArgumentType, "dependencyObject", typeof(Activity).ToString())); 3147 3148 Activity currentActivity = (Activity)dependencyObject; 3149 3150 // fetch workflow executor 3151 IWorkflowCoreRuntime workflowExecutor = null; 3152 ISupportInterop interopSupport = null; 3153 if (currentActivity != null) 3154 { 3155 workflowExecutor = ContextActivityUtils.RetrieveWorkflowExecutor(currentActivity); 3156 interopSupport = workflowExecutor as ISupportInterop; 3157 } 3158 3159 while (currentActivity != null) 3160 { 3161 // If the current activity has a batch property, use it. 3162 IWorkBatch transientWorkBatch = currentActivity.GetValueBase(TransientBatchProperty) as IWorkBatch; 3163 if (transientWorkBatch != null) 3164 return transientWorkBatch; 3165 3166 // If it's a transactional activity (transactional scope), create a batch for it. 3167 // (If the activity is not executing, it means that it has canceled, probably 3168 // due to an exception. In this case, we do not create the batch here, but keep 3169 // looking up until we find an appropriate scope, or the root.) 3170 if (TransactedContextFilter.GetTransactionOptions(currentActivity) != null && currentActivity.ExecutionStatus == ActivityExecutionStatus.Executing) 3171 return interopSupport.BatchCollection.GetBatch(currentActivity); 3172 3173 // if activity has a fault handler create a batch for it. 3174 if (currentActivity is CompositeActivity) 3175 { 3176 foreach (Activity flowActivity in ((ISupportAlternateFlow)currentActivity).AlternateFlowActivities) 3177 { 3178 if (flowActivity is FaultHandlerActivity) 3179 return interopSupport.BatchCollection.GetBatch(currentActivity); 3180 } 3181 } 3182 3183 // If it's the root activity, create a batch for it. Note that we'll only 3184 // ever get here if the root activity is not also an exception handling activity. 3185 if (currentActivity == workflowExecutor.RootActivity) 3186 return interopSupport.BatchCollection.GetBatch(currentActivity); 3187 3188 currentActivity = currentActivity.Parent; 3189 } 3190 3191 return null; 3192 } 3193 GetNestedExceptionMessage(Exception exp)3194 private static string GetNestedExceptionMessage(Exception exp) 3195 { 3196 string expMessage = ""; 3197 while (exp != null) 3198 { 3199 if (expMessage == "") 3200 expMessage = exp.Message; 3201 else 3202 expMessage = expMessage + " " + exp.Message; 3203 exp = exp.InnerException; 3204 } 3205 return expMessage; 3206 } 3207 3208 #region Internal Events 3209 3210 internal class WorkflowExecutionEventArgs : EventArgs 3211 { 3212 protected WorkflowEventInternal _eventType; 3213 WorkflowExecutionEventArgs()3214 protected WorkflowExecutionEventArgs() { } 3215 WorkflowExecutionEventArgs(WorkflowEventInternal eventType)3216 internal WorkflowExecutionEventArgs(WorkflowEventInternal eventType) 3217 { 3218 _eventType = eventType; 3219 } 3220 3221 internal WorkflowEventInternal EventType 3222 { 3223 get { return _eventType; } 3224 } 3225 } 3226 private event EventHandler<WorkflowExecutionEventArgs> _workflowExecutionEvent; 3227 3228 internal class WorkflowHandlerInvokingEventArgs : WorkflowExecutionEventArgs 3229 { 3230 private Delegate _delegateHandler; 3231 WorkflowHandlerInvokingEventArgs(WorkflowEventInternal eventType, Delegate delegateHandler)3232 internal WorkflowHandlerInvokingEventArgs(WorkflowEventInternal eventType, Delegate delegateHandler) 3233 : base(eventType) 3234 { 3235 _delegateHandler = delegateHandler; 3236 } 3237 3238 internal Delegate DelegateMethod 3239 { 3240 get { return _delegateHandler; } 3241 } 3242 } 3243 3244 /// <summary> 3245 /// Consolidated event for the majority of the general events. 3246 /// Filter specific events by WorkflowEventEventArgs.EventType. 3247 /// </summary> 3248 internal event EventHandler<WorkflowExecutor.WorkflowExecutionEventArgs> WorkflowExecutionEvent 3249 { 3250 add 3251 { 3252 _workflowExecutionEvent += value; 3253 } 3254 remove 3255 { 3256 _workflowExecutionEvent -= value; 3257 } 3258 } 3259 FireWorkflowExecutionEvent(object sender, WorkflowEventInternal eventType)3260 internal void FireWorkflowExecutionEvent(object sender, WorkflowEventInternal eventType) 3261 { 3262 if (null == sender) 3263 sender = this; 3264 3265 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3266 if (null != localWorkflowExecutionEvent) 3267 localWorkflowExecutionEvent(sender, new WorkflowExecutionEventArgs(eventType)); 3268 } 3269 FireWorkflowHandlerInvokingEvent(object sender, WorkflowEventInternal eventType, Delegate delegateHandler)3270 internal void FireWorkflowHandlerInvokingEvent(object sender, WorkflowEventInternal eventType, Delegate delegateHandler) 3271 { 3272 if (null == sender) 3273 sender = this; 3274 3275 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3276 if (null != localWorkflowExecutionEvent) 3277 localWorkflowExecutionEvent(sender, new WorkflowHandlerInvokingEventArgs(eventType, delegateHandler)); 3278 } 3279 3280 internal sealed class WorkflowExecutionSuspendingEventArgs : WorkflowExecutionEventArgs 3281 { 3282 private string _error; 3283 WorkflowExecutionSuspendingEventArgs(string error)3284 internal WorkflowExecutionSuspendingEventArgs(string error) 3285 { 3286 _eventType = WorkflowEventInternal.Suspending; 3287 _error = error; 3288 } 3289 3290 internal string Error 3291 { 3292 get { return _error; } 3293 } 3294 } 3295 3296 internal sealed class WorkflowExecutionSuspendedEventArgs : WorkflowExecutionEventArgs 3297 { 3298 private string _error; 3299 WorkflowExecutionSuspendedEventArgs(string error)3300 internal WorkflowExecutionSuspendedEventArgs(string error) 3301 { 3302 _eventType = WorkflowEventInternal.Suspended; 3303 _error = error; 3304 } 3305 3306 internal string Error 3307 { 3308 get { return _error; } 3309 } 3310 } 3311 /// <summary> 3312 /// Fires the WorkflowEvent with an EventType of Suspended and WorkflowSuspendedInternalEventArgs 3313 /// </summary> 3314 /// <param name="info">Reason for the suspension</param> FireWorkflowSuspending(string error)3315 private void FireWorkflowSuspending(string error) 3316 { 3317 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3318 if (null != localWorkflowExecutionEvent) 3319 localWorkflowExecutionEvent(this, new WorkflowExecutionSuspendingEventArgs(error)); 3320 } 3321 3322 /// <summary> 3323 /// Fires the WorkflowEvent with an EventType of Suspended and WorkflowSuspendInternalEventArgs 3324 /// </summary> 3325 /// <param name="info">Reason for the suspension.</param> FireWorkflowSuspended(string error)3326 internal void FireWorkflowSuspended(string error) 3327 { 3328 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3329 if (null != localWorkflowExecutionEvent) 3330 localWorkflowExecutionEvent(this, new WorkflowExecutionSuspendedEventArgs(error)); 3331 } 3332 3333 3334 internal class WorkflowExecutionExceptionEventArgs : WorkflowExecutionEventArgs 3335 { 3336 private System.Exception _exception; 3337 private string _currentPath, _originalPath; 3338 private Guid _contextGuid, _parentContextGuid; 3339 WorkflowExecutionExceptionEventArgs(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid)3340 internal WorkflowExecutionExceptionEventArgs(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid) 3341 { 3342 if (null == exception) 3343 throw new ArgumentNullException("exception"); 3344 3345 _exception = exception; 3346 _currentPath = currentPath; 3347 _originalPath = originalPath; 3348 _eventType = WorkflowEventInternal.Exception; 3349 _contextGuid = contextGuid; 3350 _parentContextGuid = parentContextGuid; 3351 } 3352 3353 internal Exception Exception 3354 { 3355 get { return _exception; } 3356 } 3357 3358 internal string CurrentPath 3359 { 3360 get { return _currentPath; } 3361 } 3362 3363 internal string OriginalPath 3364 { 3365 get { return _originalPath; } 3366 } 3367 3368 internal Guid ContextGuid 3369 { 3370 get { return _contextGuid; } 3371 } 3372 3373 internal Guid ParentContextGuid 3374 { 3375 get { return _parentContextGuid; } 3376 } 3377 } 3378 /// <summary> 3379 /// Fires the WorkflowEvent with an EventType of Exception and WorkflowExceptionInternalEventArgs 3380 /// </summary> 3381 /// <param name="exception">Thrown exception</param> FireWorkflowException(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid)3382 private void FireWorkflowException(Exception exception, string currentPath, string originalPath, Guid contextGuid, Guid parentContextGuid) 3383 { 3384 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3385 if (null != localWorkflowExecutionEvent) 3386 localWorkflowExecutionEvent(this, new WorkflowExecutionExceptionEventArgs(exception, currentPath, originalPath, contextGuid, parentContextGuid)); 3387 } 3388 3389 3390 internal sealed class WorkflowExecutionTerminatedEventArgs : WorkflowExecutionEventArgs 3391 { 3392 private System.Exception _exception; 3393 private string _error; 3394 WorkflowExecutionTerminatedEventArgs(string error)3395 internal WorkflowExecutionTerminatedEventArgs(string error) 3396 { 3397 _error = error; 3398 _eventType = WorkflowEventInternal.Terminated; 3399 } 3400 WorkflowExecutionTerminatedEventArgs(Exception exception)3401 internal WorkflowExecutionTerminatedEventArgs(Exception exception) 3402 { 3403 _exception = exception; 3404 _eventType = WorkflowEventInternal.Terminated; 3405 } 3406 3407 internal Exception Exception 3408 { 3409 get { return _exception; } 3410 } 3411 3412 internal string Error 3413 { 3414 get { return _error; } 3415 } 3416 } 3417 internal sealed class WorkflowExecutionTerminatingEventArgs : WorkflowExecutionEventArgs 3418 { 3419 private System.Exception _exception; 3420 private string _error; 3421 WorkflowExecutionTerminatingEventArgs(string error)3422 internal WorkflowExecutionTerminatingEventArgs(string error) 3423 { 3424 _error = error; 3425 _eventType = WorkflowEventInternal.Terminating; 3426 } 3427 WorkflowExecutionTerminatingEventArgs(Exception exception)3428 internal WorkflowExecutionTerminatingEventArgs(Exception exception) 3429 { 3430 if (null == exception) 3431 throw new ArgumentNullException("exception"); 3432 3433 _exception = exception; 3434 _eventType = WorkflowEventInternal.Terminating; 3435 } 3436 3437 internal Exception Exception 3438 { 3439 get { return _exception; } 3440 } 3441 3442 internal string Error 3443 { 3444 get { return _error; } 3445 } 3446 } 3447 /// <summary> 3448 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs 3449 /// </summary> 3450 /// <param name="exception">Exception that caused the termination</param> FireWorkflowTerminating(Exception exception)3451 private void FireWorkflowTerminating(Exception exception) 3452 { 3453 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3454 if (null != localWorkflowExecutionEvent) 3455 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatingEventArgs(exception)); 3456 } 3457 /// <summary> 3458 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs 3459 /// </summary> 3460 /// <param name="info">Reason for the termination</param> FireWorkflowTerminating(string error)3461 private void FireWorkflowTerminating(string error) 3462 { 3463 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3464 if (null != localWorkflowExecutionEvent) 3465 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatingEventArgs(error)); 3466 } 3467 /// <summary> 3468 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs 3469 /// </summary> 3470 /// <param name="exception">Exception that caused the termination</param> FireWorkflowTerminated(Exception exception)3471 internal void FireWorkflowTerminated(Exception exception) 3472 { 3473 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3474 if (null != localWorkflowExecutionEvent) 3475 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatedEventArgs(exception)); 3476 } 3477 /// <summary> 3478 /// Fires the WorkflowEvent with an EventType of Terminated and WorkflowTerminatedInternalEventArgs 3479 /// </summary> 3480 /// <param name="info">Reason for the termination</param> FireWorkflowTerminated(string error)3481 internal void FireWorkflowTerminated(string error) 3482 { 3483 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3484 if (null != localWorkflowExecutionEvent) 3485 localWorkflowExecutionEvent(this, new WorkflowExecutionTerminatedEventArgs(error)); 3486 } 3487 3488 3489 3490 internal class DynamicUpdateEventArgs : WorkflowExecutionEventArgs 3491 { 3492 private IList<WorkflowChangeAction> _changeActions = new List<WorkflowChangeAction>(); 3493 DynamicUpdateEventArgs(IList<WorkflowChangeAction> changeActions, WorkflowEventInternal eventType)3494 internal DynamicUpdateEventArgs(IList<WorkflowChangeAction> changeActions, WorkflowEventInternal eventType) 3495 { 3496 _changeActions = changeActions; 3497 _eventType = eventType; 3498 } 3499 3500 internal IList<WorkflowChangeAction> ChangeActions 3501 { 3502 get { return _changeActions; } 3503 } 3504 } 3505 /// <summary> 3506 /// Signals that a dynamic update is starting. 3507 /// </summary> FireDynamicUpdateBegin(IList<WorkflowChangeAction> changeActions)3508 private void FireDynamicUpdateBegin(IList<WorkflowChangeAction> changeActions) 3509 { 3510 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3511 if (null != localWorkflowExecutionEvent) 3512 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeBegin)); 3513 } 3514 3515 3516 /// <summary> 3517 /// Signals that a dynamic update has errored and rolledback. 3518 /// </summary> FireDynamicUpdateRollback(IList<WorkflowChangeAction> changeActions)3519 private void FireDynamicUpdateRollback(IList<WorkflowChangeAction> changeActions) 3520 { 3521 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3522 if (null != localWorkflowExecutionEvent) 3523 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeRollback)); 3524 } 3525 3526 3527 /// <summary> 3528 /// Signals that a dynamic update has completed successfully. 3529 /// </summary> FireDynamicUpdateCommit(IList<WorkflowChangeAction> changeActions)3530 private void FireDynamicUpdateCommit(IList<WorkflowChangeAction> changeActions) 3531 { 3532 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3533 if (null != localWorkflowExecutionEvent) 3534 localWorkflowExecutionEvent(this, new DynamicUpdateEventArgs(changeActions, WorkflowEventInternal.DynamicChangeCommit)); 3535 } 3536 3537 internal class ActivityStatusChangeEventArgs : WorkflowExecutionEventArgs 3538 { 3539 private Activity _activity; 3540 ActivityStatusChangeEventArgs(Activity activity)3541 internal ActivityStatusChangeEventArgs(Activity activity) 3542 { 3543 _activity = activity; 3544 _eventType = WorkflowEventInternal.ActivityStatusChange; 3545 } 3546 3547 internal Activity Activity 3548 { 3549 get { return _activity; } 3550 } 3551 } 3552 3553 internal class ActivityExecutingEventArgs : WorkflowExecutionEventArgs 3554 { 3555 private Activity _activity; 3556 ActivityExecutingEventArgs(Activity activity)3557 internal ActivityExecutingEventArgs(Activity activity) 3558 { 3559 _activity = activity; 3560 _eventType = WorkflowEventInternal.ActivityExecuting; 3561 } 3562 3563 internal Activity Activity 3564 { 3565 get { return _activity; } 3566 } 3567 } 3568 /// <summary> 3569 /// Signals that an activity has changed status. 3570 /// This event applies to all status change events 3571 /// for all activities in the workflow. 3572 /// </summary> FireActivityStatusChange(object sender, Activity activity)3573 private void FireActivityStatusChange(object sender, Activity activity) 3574 { 3575 ActivityStatusChangeEventArgs args = new ActivityStatusChangeEventArgs(activity); 3576 3577 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3578 if (null != localWorkflowExecutionEvent) 3579 localWorkflowExecutionEvent(sender, args); 3580 } 3581 FireActivityExecuting(object sender, Activity activity)3582 private void FireActivityExecuting(object sender, Activity activity) 3583 { 3584 ActivityExecutingEventArgs args = new ActivityExecutingEventArgs(activity); 3585 3586 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3587 if (null != localWorkflowExecutionEvent) 3588 localWorkflowExecutionEvent(sender, args); 3589 } 3590 3591 internal class UserTrackPointEventArgs : WorkflowExecutionEventArgs 3592 { 3593 Activity _activity; 3594 string _key; 3595 object _args; 3596 UserTrackPointEventArgs(Activity activity, string key, object args)3597 internal UserTrackPointEventArgs(Activity activity, string key, object args) 3598 { 3599 if (null == activity) 3600 throw new ArgumentNullException("activity"); 3601 3602 _activity = activity; 3603 // 3604 // args may be null, user code can send non null value 3605 _args = args; 3606 _eventType = WorkflowEventInternal.UserTrackPoint; 3607 _key = key; 3608 } 3609 3610 internal Activity Activity 3611 { 3612 get { return _activity; } 3613 } 3614 3615 internal string Key 3616 { 3617 get { return _key; } 3618 } 3619 3620 internal object Args 3621 { 3622 get { return _args; } 3623 } 3624 } 3625 FireUserTrackPoint(Activity activity, string key, object args)3626 private void FireUserTrackPoint(Activity activity, string key, object args) 3627 { 3628 EventHandler<WorkflowExecutionEventArgs> localWorkflowExecutionEvent = this._workflowExecutionEvent; 3629 if (null != localWorkflowExecutionEvent) 3630 localWorkflowExecutionEvent(this, new UserTrackPointEventArgs(activity, key, args)); 3631 } 3632 3633 3634 #endregion Internal Events 3635 } 3636 3637 internal class ScheduleWork : IDisposable 3638 { 3639 internal class ScheduleInfo 3640 { 3641 public bool scheduleWork; 3642 public WorkflowExecutor executor; 3643 public bool suppress; ScheduleInfo(WorkflowExecutor executor, bool suppress)3644 public ScheduleInfo(WorkflowExecutor executor, bool suppress) 3645 { 3646 this.suppress = suppress; 3647 scheduleWork = false; 3648 this.executor = executor; 3649 } 3650 } 3651 [ThreadStatic] 3652 protected static ScheduleInfo scheduleInfo; 3653 protected ScheduleInfo oldValue; 3654 ScheduleWork(WorkflowExecutor executor)3655 public ScheduleWork(WorkflowExecutor executor) 3656 { 3657 oldValue = scheduleInfo; 3658 scheduleInfo = new ScheduleInfo(executor, false); 3659 } 3660 ScheduleWork(WorkflowExecutor executor, bool suppress)3661 public ScheduleWork(WorkflowExecutor executor, bool suppress) 3662 { 3663 oldValue = scheduleInfo; 3664 scheduleInfo = new ScheduleInfo(executor, suppress); 3665 } 3666 3667 static public bool NeedsService 3668 { 3669 // get 3670 // { 3671 // Debug.Assert(ScheduleWork.scheduleInfo != null); 3672 // return ScheduleWork.scheduleInfo.scheduleWork; 3673 // } 3674 set 3675 { 3676 Debug.Assert(ScheduleWork.scheduleInfo != null); 3677 Debug.Assert(value == true || ScheduleWork.scheduleInfo.scheduleWork == false); // never go from true to false 3678 ScheduleWork.scheduleInfo.scheduleWork = value; 3679 } 3680 } 3681 static public WorkflowExecutor Executor 3682 { 3683 // get 3684 // { 3685 // Debug.Assert(ScheduleWork.scheduleInfo != null); 3686 // return ScheduleWork.scheduleInfo.executor; 3687 // } 3688 set 3689 { 3690 Debug.Assert(ScheduleWork.scheduleInfo != null); 3691 ScheduleWork.scheduleInfo.executor = value; 3692 } 3693 } 3694 #region IDisposable Members 3695 Dispose()3696 public virtual void Dispose() 3697 { 3698 if ((scheduleInfo.scheduleWork) && (!scheduleInfo.suppress)) 3699 { 3700 scheduleInfo.executor.RequestHostingService(); 3701 } 3702 scheduleInfo = oldValue; 3703 } 3704 3705 #endregion 3706 } 3707 } 3708