#region --------------- Copyright Dresser Wayne Pignone ------------- /* * $Log: /Wrk/WayneLibraries/Wrk/StateEngine/Generic/AsyncWorkState.cs $ * * 5 08-03-25 10:32 Mattias.larsson * Ensure debugLogger is not null. * * 4 07-11-28 15:40 roger.månsson * * 3 07-10-23 13:47 roger.månsson * Must protect the access to the field currentWork, or there may be race * conditions, the field is maybe not be assigned until after the work is * done for example. * * 2 07-10-17 13:20 roger.månsson * Made publicly available. Added overridable method WorkDone. * * 1 07-03-27 9:33 roger.månsson * Created. */ #endregion using System; using System.Collections.Generic; using Wayne.Lib; using Wayne.Lib.StateEngine; using System.Threading; namespace Wayne.Lib.StateEngine.Generic { /// /// Generic state that enables descendant classes to execute code on a worker thread /// when the state is active. When the processing is completed, i.e. the PerformWork returns, /// the state will post a transition of the type specified in the constructor. /// /// When exitting the state, we for the worker thread to complete before continuing. /// /// Descendant classes can also use the AbortWork() method to signal to the worker thread that it should exit as fast as possible. /// /// The PerformWork method should periodically check the Aborted property and if that is true, exit as fast as possible. /// /// public abstract class AsyncWorkState : State { #region Fields bool aborted; object doneTransitionType; object runningLock = new object(); #endregion #region Construction /// /// Initializes a new instance of the AsyncWorkState class. /// protected AsyncWorkState(object doneTransitionType) { this.doneTransitionType = doneTransitionType; } #endregion #region Methods /// /// Called when entering the state. /// /// /// protected override void Enter(Wayne.Lib.StateEngine.StateEntry stateEntry, ref Wayne.Lib.StateEngine.Transition transition) { base.Enter(stateEntry, ref transition); if (transition == null) { aborted = false; ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), null); } } private void DoWork(object o) { try { try { if (aborted) return; lock (runningLock) { PerformWork(); } } catch (Exception e) { if ((ParentStateMachine.debugLogger != null) && ParentStateMachine.debugLogger.IsActive()) ParentStateMachine.debugLogger.Add(e); } } finally { this.ParentStateMachine.IncomingEvent(new StateEngineEvent(GenericEventType.AsyncDone)); } } /// /// Called when exitting the state. /// protected override void Exit() { base.Exit(); aborted = true; lock (runningLock) { } } /// /// Handles events. /// /// /// protected override void HandleEvent(StateEngineEvent stateEngineEvent, ref Transition transition) { base.HandleEvent(stateEngineEvent, ref transition); if (stateEngineEvent.Type.Equals(GenericEventType.AsyncDone)) { stateEngineEvent.Handled = true; WorkDone(ref transition); } } /// /// Method that is called when the work is done. Override to specify another transition type than the one /// specified in the constructor. /// /// protected virtual void WorkDone(ref Transition transition) { transition = new Transition(this, doneTransitionType); } /// /// Signals to the thread that it should exit as quick as possible. Can be overridden by descendant /// classes to create user code to abort. /// protected virtual void AbortWork() { aborted = true; } /// /// Signals that the PerformWork method should return as fast as possible. /// protected bool Aborted { get { return aborted; } } #endregion #region Abstract methods /// /// Method that is called in a worker thread. /// protected abstract void PerformWork(); #endregion } }