view Implab.Diagnostics.Interactive/InteractiveListener.cs @ 66:790e8a997d30

Refactoring
author cin
date Thu, 14 Aug 2014 18:08:09 +0400
parents d9d794b61bb9
children abe260860bd6
line wrap: on
line source

using Implab.Parallels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Implab.Diagnostics.Interactive
{
    public class InteractiveListener: TextListenerBase
    {
        TraceForm m_form;
        
        SynchronizationContext m_syncGuiThread;
        readonly Promise<object> m_guiStarted = new Promise<object>();
        
        readonly IPromise m_guiFinished;
        readonly IPromise m_workerFinished = new Promise<object>();
        
        readonly MTQueue<TraceViewItem> m_queue = new MTQueue<TraceViewItem>();
        readonly AutoResetEvent m_queueEvent = new AutoResetEvent(false);
        
        int m_queueLength;
        bool m_exitPending;

        readonly object m_pauseLock = new object();
        bool m_paused;
        readonly ManualResetEvent m_pauseEvent = new ManualResetEvent(true);

        public InteractiveListener(bool global) : base(global) {
            m_guiFinished = AsyncPool.InvokeNewThread(GuiThread);
            m_workerFinished = AsyncPool.InvokeNewThread(QueueThread);

            m_guiStarted.Join();
        }

        void GuiThread() {
            m_form = new TraceForm(); // will create SynchronizationContext

            m_form.PauseEvents += (s,a) => Pause();
            m_form.ResumeEvents += (s, a) => Resume();

            m_syncGuiThread = SynchronizationContext.Current;
            m_guiStarted.Resolve();
            Application.Run();
        }

        void QueueThread() {
            while (!m_exitPending) {
                if (m_paused)
                    m_pauseEvent.WaitOne();
                
                TraceViewItem item;
                if (m_queue.TryDequeue(out item)) {
                    Interlocked.Decrement(ref m_queueLength);

                    m_syncGuiThread.Send(x => m_form.AddTraceEvent(item),null);
                } else {
                    m_queueEvent.WaitOne();
                }
            }
        }

        public void Pause() {
            // for consistency we need to set this properties atomically
            lock (m_pauseLock) {
                m_pauseEvent.Reset();
                m_paused = true;
            }
        }

        public void Resume() {
            // for consistency we need to set this properties atomically
            lock (m_pauseLock) {
                m_paused = false;
                m_pauseEvent.Set();
            }
        }

        void Enqueue(TraceViewItem item) {
            m_queue.Enqueue(item);
            if (Interlocked.Increment(ref m_queueLength) == 1)
                m_queueEvent.Set();
        }

        public void ShowForm() {
            m_syncGuiThread.Post(x => m_form.Show(), null);
        }

        public void HideForm() {
            m_syncGuiThread.Post(x => m_form.Hide(), null);
        }

        void Terminate() {
            m_exitPending = true;
            Resume();            
            m_syncGuiThread.Post(x => Application.ExitThread(), null);
        }

        protected override void Dispose(bool disposing) {
            if (disposing) {
                Terminate();
                m_guiFinished.Join();
            }
            base.Dispose(disposing);
        }

        protected override void WriteEntry(TraceContext context, EventText text, string channel) {
            var item = new TraceViewItem {
                Indent = text.indent,
                Message = text.content,
                Thread = context.ThreadId,
                Channel = channel,
                Timestamp = Environment.TickCount
            };

            Enqueue(item);
        }
    }
}