RunnableComponent.cs
195 lines
| 5.5 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r156 | using System; | ||
cin
|
r180 | using Implab.Formats; | ||
cin
|
r156 | |||
namespace Implab.Components { | ||||
public class RunnableComponent : Disposable, IRunnable, IInitializable { | ||||
cin
|
r184 | enum Commands { | ||
Ok = 0, | ||||
Fail, | ||||
Init, | ||||
Start, | ||||
Stop, | ||||
Dispose, | ||||
Last = Dispose | ||||
} | ||||
class StateMachine { | ||||
static readonly ExecutionState[,] _transitions; | ||||
static StateMachine() { | ||||
_transitions = new ExecutionState[(int)ExecutionState.Last + 1, (int)Commands.Last + 1]; | ||||
Edge(ExecutionState.Created, ExecutionState.Ready, Commands.Ok); | ||||
Edge(ExecutionState.Created, ExecutionState.Failed, Commands.Fail); | ||||
Edge(ExecutionState.Ready, ExecutionState.Starting, Commands.Start); | ||||
Edge(ExecutionState.Ready, ExecutionState.Disposed, Commands.Dispose); | ||||
Edge(ExecutionState.Starting, ExecutionState.Running, Commands.Ok); | ||||
Edge(ExecutionState.Starting, ExecutionState.Failed, Commands.Fail); | ||||
Edge(ExecutionState.Starting, ExecutionState.Stopping, Commands.Stop); | ||||
Edge(ExecutionState.Starting, ExecutionState.Disposed, Commands.Dispose); | ||||
Edge(ExecutionState.Running, ExecutionState.Failed, Commands.Fail); | ||||
Edge(ExecutionState.Running, ExecutionState.Stopping, Commands.Stop); | ||||
Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose); | ||||
Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail); | ||||
Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok); | ||||
Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose); | ||||
} | ||||
static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) { | ||||
_transitions[(int)s1, (int)cmd] = s2; | ||||
} | ||||
public ExecutionState State { | ||||
get; | ||||
private set; | ||||
} | ||||
public StateMachine(ExecutionState initial) { | ||||
State = initial; | ||||
} | ||||
public bool Move(Commands cmd) { | ||||
var next = _transitions[(int)State, (int)cmd]; | ||||
if (next == ExecutionState.Undefined) | ||||
return false; | ||||
State = next; | ||||
return true; | ||||
} | ||||
} | ||||
IPromise m_pending; | ||||
Exception m_lastError; | ||||
readonly StateMachine m_stateMachine; | ||||
protected RunnableComponent(bool initialized) { | ||||
m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created); | ||||
} | ||||
void ThrowInvalidCommand(Commands cmd) { | ||||
throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State)); | ||||
} | ||||
protected void Move(Commands cmd) { | ||||
lock (m_stateMachine) | ||||
if (!m_stateMachine.Move(cmd)) | ||||
ThrowInvalidCommand(cmd); | ||||
} | ||||
protected void Fail(Exception err) { | ||||
lock (m_stateMachine) { | ||||
if (!m_stateMachine.Move(Commands.Fail)) | ||||
ThrowInvalidCommand(Commands.Fail); | ||||
m_lastError = err; | ||||
} | ||||
} | ||||
protected void Success() { | ||||
Move(Commands.Ok); | ||||
} | ||||
protected void Invoke(Commands cmd, Action action) { | ||||
Move(cmd); | ||||
try { | ||||
action(); | ||||
Move(Commands.Ok); | ||||
} catch (Exception err) { | ||||
Fail(err); | ||||
throw; | ||||
} | ||||
} | ||||
protected IPromise InvokeAsync(Commands cmd, Func<IPromise> action) { | ||||
Move(cmd); | ||||
var medium = new Promise(); | ||||
IPromise promise = null; | ||||
promise = medium.Then( | ||||
() => { | ||||
lock(m_stateMachine) { | ||||
if (m_pending == promise) { | ||||
m_pending = null; | ||||
Move(Commands.Ok); | ||||
} | ||||
} | ||||
}, e => { | ||||
if (m_pending == promise) { | ||||
m_pending = null; | ||||
Fail( | ||||
} | ||||
} | ||||
); | ||||
cin
|
r157 | |||
cin
|
r156 | |||
cin
|
r184 | return Safe.InvokePromise(action).Then( | ||
Success, | ||||
Fail | ||||
); | ||||
} | ||||
cin
|
r156 | |||
cin
|
r184 | void AddPending(IPromise result) { | ||
cin
|
r156 | } | ||
cin
|
r184 | |||
cin
|
r156 | #region IInitializable implementation | ||
public void Init() { | ||||
cin
|
r184 | Invoke(Commands.Init, OnInitialize); | ||
} | ||||
protected virtual void OnInitialize() { | ||||
cin
|
r156 | } | ||
#endregion | ||||
#region IRunnable implementation | ||||
public IPromise Start() { | ||||
cin
|
r184 | Move(Commands.Start); | ||
return Safe.InvokePromise(OnStart).Then( | ||||
() => { | ||||
Move(Commands.Ok); | ||||
Run(); | ||||
}, | ||||
() => { | ||||
Move(Commands.Fail); | ||||
} | ||||
); | ||||
cin
|
r156 | } | ||
protected virtual IPromise OnStart() { | ||||
return Promise.SUCCESS; | ||||
} | ||||
protected virtual void Run() { | ||||
} | ||||
public IPromise Stop() { | ||||
throw new NotImplementedException(); | ||||
} | ||||
public ExecutionState State { | ||||
get { | ||||
throw new NotImplementedException(); | ||||
} | ||||
} | ||||
public Exception LastError { | ||||
get { | ||||
throw new NotImplementedException(); | ||||
} | ||||
} | ||||
#endregion | ||||
} | ||||
} | ||||