diff --git a/Implab/Components/RunnableComponent.cs b/Implab/Components/RunnableComponent.cs --- a/Implab/Components/RunnableComponent.cs +++ b/Implab/Components/RunnableComponent.cs @@ -12,7 +12,7 @@ namespace Implab.Components { /// This class provides a basic lifecycle from the creation to the /// termination of the component. /// - public class RunnableComponent : IAsyncComponent, IRunnable, IInitializable, IDisposable { + public abstract class RunnableComponent : IAsyncComponent, IRunnable, IInitializable, IDisposable { /// /// This class bounds lifetime to the task, @@ -93,6 +93,15 @@ namespace Implab.Components { ExecutionState m_state; + /// + /// Объект синхронизации используется для обеспечения совместного доступа + /// клиента компоненты и процессов, протекающих внутри компоненты, к общему + /// состоянию, т.е.true таким свойствам, как , + /// . Обработчики события + /// вызываются уже с установленной блокировкой, поэтому дополнительная + /// синхронизация не требуется. + /// + public object SynchronizationObject { get { return m_lock; } } protected RunnableComponent(bool initialized) { State = initialized ? ExecutionState.Ready : ExecutionState.Created; @@ -117,6 +126,11 @@ namespace Implab.Components { public Exception LastError { get; private set; } + /// + /// Событие изменения состояния компоненты.see Обработчики данного события + /// вызываются внутри блокировки и должны + /// выполняться максимально быстро. + /// public event EventHandler StateChanged; /// @@ -130,6 +144,13 @@ namespace Implab.Components { /// public void Dispose() { bool dispose = false; + lock (SynchronizationObject) { + if (m_state != ExecutionState.Disposed) { + dispose = true; + m_state = ExecutionState.Disposed; + m_cookie = new object(); + } + } if (dispose) { Dispose(true); GC.SuppressFinalize(this); @@ -152,7 +173,7 @@ namespace Implab.Components { public void Initialize() { var cookie = new object(); if (MoveInitialize(cookie)) - ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie); + Safe.NoWait(ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie)); else throw new InvalidOperationException(); } @@ -173,19 +194,36 @@ namespace Implab.Components { public void Start(CancellationToken ct) { var cookie = new object(); if (MoveStart(cookie)) - ScheduleTask(StartInternalAsync, ct, cookie); + Safe.NoWait(ScheduleStartAndRun(ct, cookie)); else throw new InvalidOperationException(); } + async Task ScheduleStartAndRun(CancellationToken ct, object cookie) { + try { + await ScheduleTask(StartInternalAsync, ct, cookie); + RunInternal(); + } catch (Exception err) { + Fail(err); + } + } + protected virtual Task StartInternalAsync(CancellationToken ct) { return Task.CompletedTask; } + /// + /// This method is called after the component is enetered running state, + /// use this method to + /// + protected virtual void RunInternal() { + + } + public void Stop(CancellationToken ct) { var cookie = new object(); if (MoveStop(cookie)) - ScheduleTask(StopAsync, ct, cookie); + Safe.NoWait(ScheduleTask(StopAsync, ct, cookie)); else throw new InvalidOperationException(); } @@ -273,9 +311,9 @@ namespace Implab.Components { } } - void ScheduleTask(Func next, CancellationToken ct, object cookie) { + Task ScheduleTask(Func next, CancellationToken ct, object cookie) { - m_current = AsyncOperationDescriptor.Create(async (x) => { + var op = AsyncOperationDescriptor.Create(async (x) => { try { await next(x); MoveSuccess(cookie); @@ -283,6 +321,9 @@ namespace Implab.Components { MoveFailed(e, cookie); } }, ct); + + m_current = op; + return op.Task; } #endregion