/// <summary> /// Callers of this method create an instance of AsyncTask derived /// class and call this method to schedule the task for execution. /// </summary> /// <param name="asyncTask">The task to execute asynchronously.</param> /// internal void ScheduleForExecution(AsyncTask asyncTask) { // When an AsyncTask is scheduled for execution during a test, it // executes on the context of the thread that runs the unit test // case (in a regular headless test case this is the unit test // background thread; in a recorded test this is the main ui thread). // if (DynamoModel.IsTestMode) { asyncTask.MarkTaskAsScheduled(); ProcessTaskInternal(asyncTask); return; } lock (taskQueue) { taskQueue.Add(asyncTask); // Append new task to the end asyncTask.MarkTaskAsScheduled(); // Update internal time-stamp. // Mark the queue as being updated. This causes the next call // to "ProcessNextTask" method to post process the task queue. taskQueueUpdated = true; // Signal task availability so scheduler picks it up. waitHandles[(int)EventIndex.TaskAvailable].Set(); } }
private void NotifyTaskStateChanged(AsyncTask task, TaskState state) { var stateChangedHandler = TaskStateChanged; if (stateChangedHandler == null) return; // No event handler, bail. var e = new TaskStateChangedEventArgs(task, state); stateChangedHandler(this, e); }
private static void ProcessTaskInternal(AsyncTask asyncTask) { try { asyncTask.Execute(); // Internally sets the ExecutionStartTime asyncTask.HandleTaskCompletion(null); // Completed successfully. } catch (Exception exception) { // HandleTaskCompletion internally sets the ExecutionEndTime time, // it also invokes the registered callback Action if there is one. // asyncTask.HandleTaskCompletion(exception); } }
private void ProcessTaskInternal(AsyncTask asyncTask) { NotifyTaskStateChanged(asyncTask, TaskState.ExecutionStarting); var executionState = asyncTask.Execute() ? TaskState.ExecutionCompleted : TaskState.ExecutionFailed; NotifyTaskStateChanged(asyncTask, executionState); asyncTask.HandleTaskCompletion(); NotifyTaskStateChanged(asyncTask, TaskState.CompletionHandled); }
private void OnPreviewGraphCompleted(AsyncTask asyncTask) { var updateTask = asyncTask as PreviewGraphAsyncTask; if (updateTask != null) { var nodeGuids = updateTask.previewGraphData; var deltaComputeStateArgs = new DeltaComputeStateEventArgs(nodeGuids,graphExecuted); OnSetNodeDeltaState(deltaComputeStateArgs); } }
protected override AsyncTask.TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) { var theOtherTask = otherTask as UpdateGraphAsyncTask; if (theOtherTask == null) return base.CanMergeWithCore(otherTask); // Comparing to another UpdateGraphAsyncTask, verify // that they are updating a similar set of nodes. if ((graphSyncData != null && graphSyncData.DeletedSubtrees != null && graphSyncData.DeletedSubtrees.Any()) || (theOtherTask.graphSyncData != null && theOtherTask.graphSyncData.DeletedSubtrees != null && theOtherTask.graphSyncData.DeletedSubtrees.Any())) return TaskMergeInstruction.KeepBoth; // Other node is either equal or a superset of this task if (ModifiedNodes.All(x => theOtherTask.ModifiedNodes.Contains(x))) { return TaskMergeInstruction.KeepOther; } // This node is a superset of the other if (theOtherTask.ModifiedNodes.All(x => ModifiedNodes.Contains(x))) { return TaskMergeInstruction.KeepThis; } // They're different, keep both return TaskMergeInstruction.KeepBoth; }
private void OnNodeValueQueried(AsyncTask asyncTask) { lock (cachedMirrorDataMutex) { var task = asyncTask as QueryMirrorDataAsyncTask; cachedMirrorData = task.MirrorData; } RaisePropertyChanged("IsUpdated"); }
protected override TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) { var theOtherTask = otherTask as UpdateRenderPackageAsyncTask; if (theOtherTask == null) return base.CanMergeWithCore(otherTask); // If the two UpdateRenderPackageAsyncTask are for different nodes, // then there is no comparison to be made, keep both the tasks. // if (nodeGuid != theOtherTask.nodeGuid) return TaskMergeInstruction.KeepBoth; // Comparing to another NotifyRenderPackagesReadyAsyncTask, the one // that gets scheduled more recently stay, while the earlier one // gets dropped. If this task has a higher tick count, keep this. // if (ScheduledTime.TickCount > theOtherTask.ScheduledTime.TickCount) return TaskMergeInstruction.KeepThis; return TaskMergeInstruction.KeepOther; // Otherwise, keep the other. }
internal TaskStateChangedEventArgs(AsyncTask task, State state) { Task = task; CurrentState = state; }
protected virtual int CompareCore(AsyncTask otherTask) { return(0); // Having the same priority by default. }
protected override int CompareCore(AsyncTask otherTask) { // PrioritizedAsyncTask always come before InconsequentialAsyncTask. if (otherTask is InconsequentialAsyncTask) return -1; // It is not compared with another PrioritizedAsyncTask, fall back. var task = otherTask as PrioritizedAsyncTask; if (task == null) return base.CompareCore(otherTask); // Priority 1 tasks always come before priority 2 tasks. return CurrPriority - task.CurrPriority; }
protected override AsyncTask.TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) { var theOtherTask = otherTask as UpdateGraphAsyncTask; if (theOtherTask == null) return base.CanMergeWithCore(otherTask); if (theOtherTask.Contains(this)) return TaskMergeInstruction.KeepOther; else if (this.Contains(theOtherTask)) return TaskMergeInstruction.KeepThis; else return TaskMergeInstruction.KeepBoth; }
protected override AsyncTask.TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) { var theOtherTask = otherTask as UpdateGraphAsyncTask; if (theOtherTask == null) return TaskMergeInstruction.KeepBoth; return TaskMergeInstruction.KeepOther; }
/// <summary> /// Call this method to determine the relative priority of two AsyncTask /// objects. DynamoScheduler makes use of this method to determine the /// order in which AsyncTask objects are sorted in its internal task queue. /// </summary> /// <param name="otherTask">A task to compare this task with.</param> /// <returns>Returns -1 if this AsyncTask object should be processed /// before the other AsyncTask; returns 1 if this AsyncTask object should /// be processed after the other AsyncTask; or 0 if both AsyncTask objects /// have the same priority and can be processed in the current order. /// </returns> /// internal int Compare(AsyncTask otherTask) { return(ReferenceEquals(this, otherTask) ? 0 : CompareCore(otherTask)); }
/// <summary> /// This method is called by DynamoScheduler when it compacts the task /// queue. The result of this call indicates if either of the tasks in /// comparison should be dropped from the task queue, or both should be /// kept. Tasks that are discarded during this phase will not be executed. /// </summary> /// <param name="otherTask">Another task to compare with.</param> /// <returns>Returns the comparison result. See Comparison enumeration /// for details of the possible values.</returns> /// internal TaskMergeInstruction CanMergeWith(AsyncTask otherTask) { return(ReferenceEquals(this, otherTask) ? TaskMergeInstruction.KeepBoth : CanMergeWithCore(otherTask)); }
/// <summary> /// Upon completion of the task, invoke the specified action /// </summary> /// <returns>An IDisposable representing the event subscription</returns> internal static IDisposable Then(this AsyncTask task, AsyncTaskCompletedHandler action) { task.Completed += action; return(Disposable.Create(() => task.Completed -= action)); }
protected virtual TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) { return(TaskMergeInstruction.KeepBoth); // Keeping both tasks by default. }
protected override TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) { var task = otherTask as InconsequentialAsyncTask; if (task == null) return base.CanMergeWithCore(otherTask); // The comparison only keeps the task that carries a bigger punch. if (Punch > task.Punch) return TaskMergeInstruction.KeepThis; return TaskMergeInstruction.KeepOther; }
/// <summary> /// An ISchedulerThread implementation calls this method so scheduler /// starts to process the next task in the queue, if there is any. Note /// that this method is meant to process only one task in queue. The /// implementation of ISchedulerThread is free to call this method again /// in a fashion that matches its task fetching behavior. /// </summary> /// <param name="waitIfTaskQueueIsEmpty">This parameter is only used if /// the task queue is empty at the time this method is invoked. When the /// task queue becomes empty, setting this to true will cause this call /// to block until either the next task becomes available, or when the /// scheduler is requested to shutdown.</param> /// <returns>This method returns true if the task queue is not empty, or /// false otherwise. Note that this method returns false when scheduler /// begins to shutdown, even when the task queue is not empty.</returns> /// public bool ProcessNextTask(bool waitIfTaskQueueIsEmpty) { AsyncTask nextTask = null; IEnumerable <AsyncTask> droppedTasks = null; lock (taskQueue) { if (taskQueueUpdated) { // The task queue has been updated since the last time // a task was processed, it might need compacting. droppedTasks = CompactTaskQueue(); ReprioritizeTasksInQueue(); taskQueueUpdated = false; } if (taskQueue.Count > 0) { nextTask = taskQueue[0]; taskQueue.RemoveAt(0); } else { // No more task in queue, reset wait handle. waitHandles[(int)EventIndex.TaskAvailable].Reset(); } } if (droppedTasks != null) { // Only notify listeners of dropping tasks here instead of // within CompactTaskQueue method. This way the lock on task // queue will not be held up for a prolonged period of time. // foreach (var droppedTask in droppedTasks) { NotifyTaskStateChanged(droppedTask, TaskState.Discarded); } } if (nextTask != null) { ProcessTaskInternal(nextTask); return(true); // This method should be called again. } // If there's no more task and wait is not desired... if (!waitIfTaskQueueIsEmpty) { return(false); // The task queue is now empty. } // Block here if ISchedulerThread requests to wait. // ReSharper disable once CoVariantArrayConversion int index = WaitHandle.WaitAny(waitHandles); // If a task becomes available, this method returns true to indicate // that an immediate call may be required (subjected to the decision // of the ISchedulerThread's implementation). In the event that the // scheduler is shutting down, then this method returns false. // return(index == ((int)EventIndex.TaskAvailable)); }
protected override int CompareCore(AsyncTask otherTask) { // PrioritizedAsyncTask always come before InconsequentialAsyncTask. if (otherTask is PrioritizedAsyncTask) return 1; // InconsequentialAsyncTask are always treated equal. return base.CompareCore(otherTask); }
/// <summary> /// This callback method is invoked in the context of ISchedulerThread /// when UpdateGraphAsyncTask is completed. /// </summary> /// <param name="task">The original UpdateGraphAsyncTask instance.</param> /// private static void OnUpdateGraphCompleted(AsyncTask task) { var updateTask = task as UpdateGraphAsyncTask; var messages = new Dictionary<Guid, string>(); // Runtime warnings take precedence over build warnings. foreach (var warning in updateTask.RuntimeWarnings) { var message = string.Join("\n", warning.Value); messages.Add(warning.Key, message); } foreach (var warning in updateTask.BuildWarnings) { // If there is already runtime warnings for // this node, then ignore the build warnings. if (messages.ContainsKey(warning.Key)) continue; var message = string.Join("\n", warning.Value); messages.Add(warning.Key, message); } var workspace = updateTask.TargetedWorkspace; foreach (var message in messages) { var guid = message.Key; var node = workspace.Nodes.FirstOrDefault(n => n.GUID == guid); if (node == null) continue; node.Warning(message.Value); // Update node warning message. } }
internal void WriteExecutionLog(AsyncTask asyncTask) { var name = asyncTask.GetType().Name; Results.Add(string.Format("{0}: {1}", name, serialNumber)); }
/// <summary> /// This callback method is invoked in the context of ISchedulerThread /// when UpdateGraphAsyncTask is completed. /// </summary> /// <param name="task">The original UpdateGraphAsyncTask instance.</param> /// private void OnUpdateGraphCompleted(AsyncTask task) { var updateTask = task as UpdateGraphAsyncTask; var messages = new Dictionary<Guid, string>(); // Runtime warnings take precedence over build warnings. foreach (var warning in updateTask.RuntimeWarnings) { var message = string.Join("\n", warning.Value.Select(w => w.Message)); messages.Add(warning.Key, message); } foreach (var warning in updateTask.BuildWarnings) { // If there is already runtime warnings for // this node, then ignore the build warnings. if (messages.ContainsKey(warning.Key)) continue; var message = string.Join("\n", warning.Value.Select(w => w.Message)); messages.Add(warning.Key, message); } var workspace = updateTask.TargetedWorkspace; foreach (var message in messages) { var guid = message.Key; var node = workspace.Nodes.FirstOrDefault(n => n.GUID == guid); if (node == null) continue; node.Warning(message.Value); // Update node warning message. } // This method is guaranteed to be called in the context of // ISchedulerThread (for Revit's case, it is the idle thread). // Dispatch the failure message display for execution on UI thread. // if (task.Exception != null && (DynamoModel.IsTestMode == false)) { Action showFailureMessage = () => Utils.DisplayEngineFailureMessage(this, task.Exception); OnRequestDispatcherBeginInvoke(showFailureMessage); } // Notify listeners (optional) of completion. OnEvaluationCompleted(this, EventArgs.Empty); }
public void TestTaskQueuePreProcessing05() { var schedulerThread = new SampleSchedulerThread(); var scheduler = new DynamoScheduler(schedulerThread, false); // Start scheduling a bunch of tasks. var asyncTasks = new AsyncTask[] { new PrioritizedAsyncTask(scheduler, 1), new InconsequentialAsyncTask(scheduler, 100), }; var results = new List<string>(); foreach (SampleAsyncTask asyncTask in asyncTasks) { asyncTask.InitializeWithResultList(results); scheduler.ScheduleForExecution(asyncTask); } schedulerThread.GetSchedulerToProcessTasks(); // Drops all InconsequentialAsyncTask and leave behind one. // Kept all PrioritizedAsyncTask instances and sorted them. Assert.AreEqual(2, results.Count); Assert.AreEqual("PrioritizedAsyncTask: 1", results[0]); Assert.AreEqual("InconsequentialAsyncTask: 100", results[1]); }
/// <summary> /// This event handler is invoked when UpdateRenderPackageAsyncTask is /// completed, at which point the render packages (specific to this node) /// become available. Since this handler is called off the UI thread, the /// '_renderPackages' must be guarded against concurrent access. /// </summary> /// <param name="asyncTask">The instance of UpdateRenderPackageAsyncTask /// that was responsible of generating the render packages.</param> /// private void OnRenderPackageUpdateCompleted(AsyncTask asyncTask) { lock (RenderPackagesMutex) { var task = asyncTask as UpdateRenderPackageAsyncTask; renderPackages.Clear(); renderPackages.AddRange(task.RenderPackages); HasRenderPackages = renderPackages.Any(); } }
public void TestTaskStateChangedEventHandling() { var observer = new TaskEventObserver(); var schedulerThread = new SampleSchedulerThread(); var scheduler = new DynamoScheduler(schedulerThread, false); scheduler.TaskStateChanged += observer.OnTaskStateChanged; // Start scheduling a bunch of tasks. var asyncTasks = new AsyncTask[] { new ErrorProneAsyncTask(scheduler, 7), new InconsequentialAsyncTask(scheduler, 100), new PrioritizedAsyncTask(scheduler, 1), new PrioritizedAsyncTask(scheduler, 5), new ErrorProneAsyncTask(scheduler, 3), new InconsequentialAsyncTask(scheduler, 500), new InconsequentialAsyncTask(scheduler, 300), new PrioritizedAsyncTask(scheduler, 3), new ErrorProneAsyncTask(scheduler, 5), }; foreach (SampleAsyncTask asyncTask in asyncTasks) scheduler.ScheduleForExecution(asyncTask); schedulerThread.GetSchedulerToProcessTasks(); // Drops all InconsequentialAsyncTask and leave behind one. // Kept all PrioritizedAsyncTask instances and sorted them. var expected = new List<string> { // Scheduling notifications... "Scheduled: ErrorProneAsyncTask: 7", "Scheduled: InconsequentialAsyncTask: 100", "Scheduled: PrioritizedAsyncTask: 1", "Scheduled: PrioritizedAsyncTask: 5", "Scheduled: ErrorProneAsyncTask: 3", "Scheduled: InconsequentialAsyncTask: 500", "Scheduled: InconsequentialAsyncTask: 300", "Scheduled: PrioritizedAsyncTask: 3", "Scheduled: ErrorProneAsyncTask: 5", // Task discarded notifications... "Discarded: InconsequentialAsyncTask: 100", "Discarded: InconsequentialAsyncTask: 300", // Execution of remaining tasks... "ExecutionStarting: ErrorProneAsyncTask: 7", "ExecutionFailed: ErrorProneAsyncTask: 7", "CompletionHandled: ErrorProneAsyncTask: 7", "ExecutionStarting: PrioritizedAsyncTask: 1", "ExecutionCompleted: PrioritizedAsyncTask: 1", "CompletionHandled: PrioritizedAsyncTask: 1", "ExecutionStarting: PrioritizedAsyncTask: 5", "ExecutionCompleted: PrioritizedAsyncTask: 5", "CompletionHandled: PrioritizedAsyncTask: 5", "ExecutionStarting: ErrorProneAsyncTask: 3", "ExecutionFailed: ErrorProneAsyncTask: 3", "CompletionHandled: ErrorProneAsyncTask: 3", "ExecutionStarting: PrioritizedAsyncTask: 3", "ExecutionCompleted: PrioritizedAsyncTask: 3", "CompletionHandled: PrioritizedAsyncTask: 3", "ExecutionStarting: ErrorProneAsyncTask: 5", "ExecutionFailed: ErrorProneAsyncTask: 5", "CompletionHandled: ErrorProneAsyncTask: 5", // Execution of InconsequentialAsyncTask last... "ExecutionStarting: InconsequentialAsyncTask: 500", "ExecutionCompleted: InconsequentialAsyncTask: 500", "CompletionHandled: InconsequentialAsyncTask: 500" }; Assert.AreEqual(expected.Count, observer.Results.Count()); int index = 0; foreach (var actual in observer.Results) { Assert.AreEqual(expected[index++], actual); } }
/// <summary> /// This callback method is invoked in the context of ISchedulerThread /// when UpdateGraphAsyncTask is completed. /// </summary> /// <param name="task">The original UpdateGraphAsyncTask instance.</param> private void OnUpdateGraphCompleted(AsyncTask task) { var updateTask = (UpdateGraphAsyncTask)task; var messages = new Dictionary<Guid, string>(); // Runtime warnings take precedence over build warnings. foreach (var warning in updateTask.RuntimeWarnings) { var message = string.Join("\n", warning.Value.Select(w => w.Message)); messages.Add(warning.Key, message); } foreach (var warning in updateTask.BuildWarnings) { // If there is already runtime warnings for // this node, then ignore the build warnings. if (messages.ContainsKey(warning.Key)) continue; var message = string.Join("\n", warning.Value.Select(w => w.Message)); messages.Add(warning.Key, message); } var workspace = updateTask.TargetedWorkspace; foreach (var message in messages) { var guid = message.Key; var node = workspace.Nodes.FirstOrDefault(n => n.GUID == guid); if (node == null) continue; node.Warning(message.Value); // Update node warning message. } // Notify listeners (optional) of completion. RunSettings.RunEnabled = true; // Re-enable 'Run' button. //set the node execution preview to false; OnSetNodeDeltaState(new DeltaComputeStateEventArgs(new List<Guid>(), graphExecuted)); // This method is guaranteed to be called in the context of // ISchedulerThread (for Revit's case, it is the idle thread). // Dispatch the failure message display for execution on UI thread. // EvaluationCompletedEventArgs e = task.Exception == null || IsTestMode ? new EvaluationCompletedEventArgs(true) : new EvaluationCompletedEventArgs(true, task.Exception); EvaluationCount ++; OnEvaluationCompleted(e); if (EngineController.IsDisposed) return; EngineController.ReconcileTraceDataAndNotify(); // Refresh values of nodes that took part in update. foreach (var modifiedNode in updateTask.ModifiedNodes) { modifiedNode.RequestValueUpdateAsync(scheduler, EngineController); } scheduler.Tasks.AllComplete(_ => { OnRefreshCompleted(e); }); }
private void OnRenderPackageAggregationCompleted(AsyncTask asyncTask) { var task = asyncTask as AggregateRenderPackageAsyncTask; var rps = new List<RenderPackage>(); rps.AddRange(task.NormalRenderPackages.Cast<RenderPackage>()); rps.AddRange(task.SelectedRenderPackages.Cast<RenderPackage>()); Debug.WriteLine(string.Format("Render aggregation complete for {0}", task.NodeId)); var e = new VisualizationEventArgs(rps, task.NodeId, -1); OnResultsReadyToVisualize(this, e); }
private void OnNodeModelRenderPackagesReady(AsyncTask asyncTask) { // By design the following method is invoked on the context of // ISchedulerThread, if access to any UI element is desired within // the method, dispatch those actions on UI dispatcher *inside* the // method, *do not* dispatch the following call here as derived // handler may need it to remain on the ISchedulerThread's context. // // Fire event to tell render targets to request their visuals OnRenderComplete(); // Call overridden method on visualization manager to // process whatever internal logic there is around // drawing a visualization. HandleRenderPackagesReadyCore(); }
private void OnRenderPackageAggregationCompleted(AsyncTask asyncTask) { var task = asyncTask as AggregateRenderPackageAsyncTask; var e = new VisualizationEventArgs(task.NormalRenderPackages, task.SelectedRenderPackages, task.NodeId); OnResultsReadyToVisualize(e); }
protected override TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) { var theOtherTask = otherTask as AggregateRenderPackageAsyncTask; if (theOtherTask == null) return base.CanMergeWithCore(otherTask); if (NodeId != theOtherTask.NodeId) return TaskMergeInstruction.KeepBoth; //// Comparing to another AggregateRenderPackageAsyncTask, the one //// that gets scheduled more recently stay, while the earlier one //// gets dropped. If this task has a higher tick count, keep this. //// if (ScheduledTime.TickCount > theOtherTask.ScheduledTime.TickCount) return TaskMergeInstruction.KeepThis; return TaskMergeInstruction.KeepOther; // Otherwise, keep the other. }