// this is called after all instances have been loaded and fixedup public void UpdateInstanceByActivityParticipation(ActivityExecutor activityExecutor, DynamicUpdateMap rootMap, ref Collection <ActivityBlockingUpdate> updateErrors) { foreach (InstanceListNeedingUpdate participant in this.updateList) { if (participant.NothingChanged || participant.ParentIdShiftOnly) { Fx.Assert(participant.UpdateMap == null && participant.MapEntry == null, "UpdateMap and MapEntry must be null if we are here."); // create a temporary NoChanges UpdateMap as well as a temporary no change MapEntry // so that we can create a NativeActivityUpdateContext object in order to invoke UpdateInstance() on an activity which // doesn't have a corresponding map and an map entry. // The scenario enabled here is scheduling a newly added reference branch to a Parallel inside an activity's implementation. participant.UpdateMap = DynamicUpdateMap.DummyMap; participant.MapEntry = DynamicUpdateMapEntry.DummyMapEntry; } // now let activities participate in update for (int i = 0; i < participant.InstanceList.Count; i++) { ActivityInstance instance = participant.InstanceList[i] as ActivityInstance; if (instance == null) { continue; } IInstanceUpdatable activity = instance.Activity as IInstanceUpdatable; if (activity != null && instance.SubState == ActivityInstance.Substate.Executing) { NativeActivityUpdateContext updateContext = new NativeActivityUpdateContext(this, activityExecutor, instance, participant.UpdateMap, participant.MapEntry, rootMap); try { activity.InternalUpdateInstance(updateContext); if (updateContext.IsUpdateDisallowed) { ActivityBlockingUpdate.AddBlockingActivity(ref updateErrors, instance.Activity, new QualifiedId(participant.OriginalId).ToString(), updateContext.DisallowedReason, instance.Id); continue; } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.NativeActivityUpdateInstanceThrewException(e.Message), e)); } finally { updateContext.Dispose(); } } } } // Schedule evaluation of newly added arguments and newly added variables. // This needs to happen after all the invokations of UpdateInstance above, so that newly // added arguments and newly added variables get evaluated before any newly added activities get executed. // We iterate the list in reverse so that parents are always scheduled after (and thus // execute before) their children, which may depend on the parents. for (int i = this.updateList.Count - 1; i >= 0; i--) { InstanceListNeedingUpdate participant = this.updateList[i]; if (!participant.MapEntryExists) { // if the given InstanceList has no map entry, // then there is no new LocationReferences to resolve. continue; } Fx.Assert(participant.MapEntry != null, "MapEntry must be non-null here."); if (!participant.MapEntry.HasEnvironmentUpdates) { // if there is no environment updates for this MapEntry, // then there is no new LocationReferences to resolve. continue; } for (int j = 0; j < participant.InstanceList.Count; j++) { ActivityInstance instance = participant.InstanceList[j] as ActivityInstance; if (instance == null || instance.SubState != ActivityInstance.Substate.Executing) { // if the given ActivityInstance is not in Substate.Executing, // then, do not try to resolve new LocationReferences continue; } List <int> addedArgumentIndexes; List <int> addedVariableIndexes; List <int> addedPrivateVariableIndexes; EnvironmentUpdateMap envMap = participant.MapEntry.EnvironmentUpdateMap; if (envMap.HasVariableEntries && TryGatherSchedulableExpressions(envMap.VariableEntries, out addedVariableIndexes)) { // schedule added variable default expressions instance.ResolveNewVariableDefaultsDuringDynamicUpdate(activityExecutor, addedVariableIndexes, false); } if (envMap.HasPrivateVariableEntries && TryGatherSchedulableExpressions(envMap.PrivateVariableEntries, out addedPrivateVariableIndexes)) { // schedule added private variable default expressions // HasPrivateMemberChanged() check disallows addition of private variable default that offsets the private IdSpace, // However, the added private variable default expression can be an imported activity, which has no affect on the private IdSpace. // For such case, we want to be able to schedule the imported default expressions here. instance.ResolveNewVariableDefaultsDuringDynamicUpdate(activityExecutor, addedPrivateVariableIndexes, true); } if (envMap.HasArgumentEntries && TryGatherSchedulableExpressions(envMap.ArgumentEntries, out addedArgumentIndexes)) { // schedule added arguments instance.ResolveNewArgumentsDuringDynamicUpdate(activityExecutor, addedArgumentIndexes); } } } }
// targetDefinition argument is optional. private IList <InstanceListNeedingUpdate> GetInstanceListsNeedingUpdate(DynamicUpdateMap updateMap, Activity targetDefinition, List <ActivityInstance> secondaryRootInstances, ref Collection <ActivityBlockingUpdate> updateErrors) { IList <InstanceListNeedingUpdate> instanceListsToUpdate = new List <InstanceListNeedingUpdate>(); if (this.rawDeserializedLists == null) { // This instance doesn't have any active instances (it is complete). return(instanceListsToUpdate); } IdSpace rootIdSpace = null; if (targetDefinition != null) { rootIdSpace = targetDefinition.MemberOf; } for (int i = 0; i < this.rawDeserializedLists.Length; i++) { InstanceList list = this.rawDeserializedLists[i]; QualifiedId oldQualifiedId = new QualifiedId(list.ActivityId); if (updateMap.IsImplementationAsRoot) { int[] oldIdArray = oldQualifiedId.AsIDArray(); if (oldIdArray.Length == 1 && oldIdArray[0] != 1) { throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidImplementationAsWorkflowRootForRuntimeState)); } } string error; InstanceListNeedingUpdate update; DynamicUpdateMap.UpdatedActivity updatedActivity = updateMap.GetUpdatedActivity(oldQualifiedId, rootIdSpace); if (CanCompensationOrConfirmationHandlerReferenceAddedSymbols(list, updateMap, rootIdSpace, secondaryRootInstances, ref updateErrors)) { update = null; } else if (updatedActivity.MapEntry == null) { if (updatedActivity.IdChanged) { // this newQualifiedId is the new id for those InstanceLists whose IDs shifted by their parents' ID change update = new InstanceListNeedingUpdate { InstanceList = list, NewId = updatedActivity.NewId }; } else { // nothing changed, no map, no mapEntry update = new InstanceListNeedingUpdate { InstanceList = list, NewId = null, }; } } else if (updatedActivity.MapEntry.IsParentRemovedOrBlocked) { update = null; } else if (IsRemovalOrRTUpdateBlockedOrBlockedByUser(updatedActivity, oldQualifiedId, out error)) { string instanceId = null; for (int j = 0; j < list.Count; j++) { ActivityInstance activityInstance = list[j] as ActivityInstance; if (activityInstance != null) { instanceId = activityInstance.Id; break; } } AddBlockingActivity(ref updateErrors, updatedActivity, oldQualifiedId, error, instanceId); update = null; } else if (IsInvalidEnvironmentUpdate(list, updatedActivity, ref updateErrors)) { update = null; } else { // no validation error for this InstanceList // add it to the list of InstanceLists to be updated update = new InstanceListNeedingUpdate { InstanceList = list, NewId = updatedActivity.NewId, UpdateMap = updatedActivity.Map, MapEntry = updatedActivity.MapEntry, NewActivity = updatedActivity.NewActivity }; } if (update != null) { update.OriginalId = list.ActivityId; instanceListsToUpdate.Add(update); } } return(instanceListsToUpdate); }