view Implab/Components/PollingRunnableComponent.cs @ 202:2651cb9a4250 v2

Implemented PollingRunnableComponent
author cin
date Tue, 18 Oct 2016 01:03:49 +0300
parents
children
line wrap: on
line source

using System;
using System.Threading;
using Implab.Diagnostics;

namespace Implab.Components {
    public class PollingRunnableComponent : RunnableComponent {
        readonly Timer m_timer;
        readonly Func<Func<IPromise>, IPromise> m_dispatcher;
        readonly TimeSpan m_interval;

        int m_processing;
        Promise m_pending;

        protected PollingRunnableComponent(TimeSpan interval, Func<Func<IPromise>, IPromise> dispatcher, bool initialized) : base(initialized) {
            m_timer = new Timer(OnInternalTick);

            m_interval = interval;
            m_dispatcher = dispatcher;
        }

        protected override IPromise OnStart() {
            m_timer.Change(TimeSpan.Zero, m_interval);

            return base.OnStart();
        }

        void OnInternalTick(object state) {
            if (StartTick()) {
                try {
                    AwaitTick(m_dispatcher != null ? m_dispatcher(OnTick) : OnTick());
                } catch (Exception error) {
                    HandleTickError(error);
                }
            }   
        }

        /// <summary>
        /// Starts the tick handler.
        /// </summary>
        /// <returns>boolean value, true - the new tick handler may be invoked, false - a tick handler is already running or a component isn't running.</returns>
        /// <remarks>
        /// If the component is stopping no new handlers can be run. Every successful call to this method must be completed with either AwaitTick or HandleTickError handlers.
        /// </remarks>
        protected virtual bool StartTick() {
            if (State == ExecutionState.Running && Interlocked.CompareExchange(ref m_processing, 1, 0) == 0) {
                m_pending = new Promise();
                m_pending
                    .On(() => m_processing = 0, PromiseEventType.All)
                    .On(null, LogTickError);
                return true;
            }
            return false;
        }

        protected virtual void AwaitTick(IPromise tick) {
            if (tick == null) {
                m_pending.Resolve();
            } else {
                tick.On(
                    m_pending.Resolve,
                    m_pending.Reject,
                    m_pending.CancelOperation
                );
                m_pending.CancellationRequested(tick.Cancel);
            }
        }

        protected virtual void HandleTickError(Exception error) {
            m_pending.Reject(error);
        }

        protected virtual void LogTickError(Exception error) {
        }

        protected virtual IPromise OnTick() {
            return Promise.SUCCESS;
        }

        protected override IPromise OnStop() {
            m_timer.Change(-1, -1);

            if (m_pending != null) {
                m_pending.Cancel();
                return m_pending.Then(base.OnStop);
            }

            return base.OnStop();
        }

        protected override void Dispose(bool disposing, Exception lastError) {
            if (disposing)
                Safe.Dispose(m_timer);
            
            base.Dispose(disposing, lastError);
        }
    }
}