//------------------------------------------------------------------------------ // <copyright file="StateMachineEventManager.cs" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> //------------------------------------------------------------------------------ namespace System.Activities.Statements { using System; using System.Activities; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Runtime.Serialization; /// <summary> /// StateMachineEventManager is used to manage triggered events globally. /// </summary> [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "This type is actually used in LINQ expression and FxCop didn't detect that.")] [DataContract] class StateMachineEventManager { // To avoid out of memory, set a fixed length of event queue. const int MaxQueueLength = 1 << 25; // queue is used to store triggered events Queue<TriggerCompletedEvent> queue; // If a state is running, its condition evaluation bookmark will be added in to activityBookmarks. // If a state is completed, its bookmark will be removed. Collection<Bookmark> activeBookmarks; /// <summary> /// Constructor to do initialization. /// </summary> public StateMachineEventManager() { this.queue = new Queue<TriggerCompletedEvent>(); this.activeBookmarks = new Collection<Bookmark>(); } /// <summary> /// Gets or sets the trigger index of current being processed event. /// </summary> [DataMember(EmitDefaultValue = false)] public TriggerCompletedEvent CurrentBeingProcessedEvent { get; set; } /// <summary> /// Gets or sets the CurrentConditionIndex denotes the index of condition is being evaluated. /// </summary> [DataMember(EmitDefaultValue = false)] public int CurrentConditionIndex { get; set; } /// <summary> /// Gets or sets a value indicating whether StateMachine is on the way of transition. /// </summary> [DataMember(EmitDefaultValue = false)] public bool OnTransition { get; set; } /// <summary> /// Gets the EventManager queue. /// </summary> public IEnumerable<TriggerCompletedEvent> Queue { get { return this.queue; } } /// <summary> /// Gets a value indicating whether StateMachineManger is ready to process an event immediately. /// </summary> bool CanProcessEventImmediately { get { return this.CurrentBeingProcessedEvent == null && !this.OnTransition && this.queue.Count == 0; } } [DataMember(EmitDefaultValue = false, Name = "queue")] internal Queue<TriggerCompletedEvent> SerializedQueue { get { return this.queue; } set { this.queue = value; } } [DataMember(EmitDefaultValue = false, Name = "activeBookmarks")] internal Collection<Bookmark> SerializedActiveBookmarks { get { return this.activeBookmarks; } set { this.activeBookmarks = value; } } /// <summary> /// When StateMachine enters a state, condition evaluation bookmark of that state would be added to activeBookmarks collection. /// </summary> /// <param name="bookmark">Bookmark reference.</param> public void AddActiveBookmark(Bookmark bookmark) { this.activeBookmarks.Add(bookmark); } /// <summary> /// Gets next completed events queue. /// </summary> /// <returns>Top TriggerCompletedEvent item in the queue.</returns> public TriggerCompletedEvent GetNextCompletedEvent() { while (this.queue.Any()) { TriggerCompletedEvent completedEvent = this.queue.Dequeue(); if (this.activeBookmarks.Contains(completedEvent.Bookmark)) { this.CurrentBeingProcessedEvent = completedEvent; return completedEvent; } } return null; } /// <summary> /// This method is used to denote whether a given bookmark is referred by currently processed event. /// </summary> /// <param name="bookmark">Bookmark reference.</param> /// <returns>True is the bookmark references to the event being processed.</returns> public bool IsReferredByBeingProcessedEvent(Bookmark bookmark) { return this.CurrentBeingProcessedEvent != null && this.CurrentBeingProcessedEvent.Bookmark == bookmark; } /// <summary> /// Register a completed event and returns whether the event could be processed immediately. /// </summary> /// <param name="completedEvent">TriggerCompletedEvent reference.</param> /// <param name="canBeProcessedImmediately">True if the Condition can be evaluated.</param> public void RegisterCompletedEvent(TriggerCompletedEvent completedEvent, out bool canBeProcessedImmediately) { canBeProcessedImmediately = this.CanProcessEventImmediately; this.queue.Enqueue(completedEvent); return; } /// <summary> /// When StateMachine leaves a state, condition evaluation bookmark of that state would be removed from activeBookmarks collection. /// </summary> /// <param name="bookmark">Bookmark reference.</param> public void RemoveActiveBookmark(Bookmark bookmark) { this.activeBookmarks.Remove(bookmark); } } }