InteractiveListener.cs
122 lines
| 3.7 KiB
| text/x-csharp
|
CSharpLexer
/ Implab.Diagnostics.Interactive / InteractiveListener.cs
|
|
r47 | 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 | ||||
| { | ||||
|
|
r134 | public class InteractiveListener: ListenerBase | ||
|
|
r47 | { | ||
| TraceForm m_form; | ||||
| SynchronizationContext m_syncGuiThread; | ||||
|
|
r130 | readonly Promise m_guiStarted = new Promise(); | ||
|
|
r47 | |||
|
|
r66 | readonly IPromise m_guiFinished; | ||
|
|
r85 | // readonly IPromise m_workerFinished = new Promise<object>(); | ||
|
|
r47 | |||
| 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); | ||||
|
|
r134 | public InteractiveListener() { | ||
|
|
r130 | m_guiFinished = AsyncPool.RunThread(GuiThread); | ||
| /*m_workerFinished = */AsyncPool.RunThread(QueueThread); | ||||
|
|
r47 | |||
| m_guiStarted.Join(); | ||||
| } | ||||
| void GuiThread() { | ||||
| m_form = new TraceForm(); // will create SynchronizationContext | ||||
|
|
r48 | |||
| m_form.PauseEvents += (s,a) => Pause(); | ||||
| m_form.ResumeEvents += (s, a) => Resume(); | ||||
|
|
r47 | 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() { | ||||
|
|
r48 | m_exitPending = true; | ||
| Resume(); | ||||
|
|
r47 | m_syncGuiThread.Post(x => Application.ExitThread(), null); | ||
| } | ||||
| protected override void Dispose(bool disposing) { | ||||
| if (disposing) { | ||||
| Terminate(); | ||||
| m_guiFinished.Join(); | ||||
| } | ||||
| base.Dispose(disposing); | ||||
| } | ||||
|
|
r48 | |||
|
|
r134 | public override void Write(LogEventArgs args, object entry) { | ||
|
|
r48 | var item = new TraceViewItem { | ||
|
|
r134 | Indent = args.Operation.Level, | ||
| Message = entry.ToString(), | ||||
|
|
r92 | Thread = args.ThreadId, | ||
|
|
r134 | Channel = args.ChannelName, | ||
|
|
r48 | Timestamp = Environment.TickCount | ||
| }; | ||||
| Enqueue(item); | ||||
| } | ||||
|
|
r47 | } | ||
| } | ||||
