InteractiveListener.cs
147 lines
| 4.5 KiB
| text/x-csharp
|
CSharpLexer
/ Implab.Diagnostics.Interactive / InteractiveListener.cs
cin
|
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 | ||||
{ | ||||
cin
|
r134 | public class InteractiveListener: ListenerBase | ||
cin
|
r47 | { | ||
TraceForm m_form; | ||||
SynchronizationContext m_syncGuiThread; | ||||
cin
|
r130 | readonly Promise m_guiStarted = new Promise(); | ||
cin
|
r47 | |||
cin
|
r66 | readonly IPromise m_guiFinished; | ||
cin
|
r47 | |||
cin
|
r239 | readonly SimpleAsyncQueue<TraceViewItem> m_queue = new SimpleAsyncQueue<TraceViewItem>(); | ||
cin
|
r47 | 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); | ||||
cin
|
r134 | public InteractiveListener() { | ||
cin
|
r215 | m_guiFinished = RunGuiThread(); | ||
AsyncPool.RunThread(QueueThread); | ||||
cin
|
r47 | |||
m_guiStarted.Join(); | ||||
} | ||||
void GuiThread() { | ||||
m_form = new TraceForm(); // will create SynchronizationContext | ||||
cin
|
r48 | |||
m_form.PauseEvents += (s,a) => Pause(); | ||||
m_form.ResumeEvents += (s, a) => Resume(); | ||||
cin
|
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); | ||||
cin
|
r214 | m_syncGuiThread.Post(x => m_form.AddTraceEvent(item),null); | ||
cin
|
r47 | } else { | ||
m_queueEvent.WaitOne(); | ||||
} | ||||
} | ||||
} | ||||
cin
|
r215 | public IPromise RunGuiThread() { | ||
var p = new Promise(); | ||||
var caller = TraceContext.Instance.CurrentOperation; | ||||
var worker = new Thread(() => { | ||||
TraceContext.Instance.EnterLogicalOperation(caller, false); | ||||
try { | ||||
Application.OleRequired(); | ||||
GuiThread(); | ||||
p.Resolve(); | ||||
} catch (Exception e) { | ||||
p.Reject(e); | ||||
} finally { | ||||
TraceContext.Instance.Leave(); | ||||
} | ||||
}); | ||||
worker.SetApartmentState(ApartmentState.STA); | ||||
worker.IsBackground = true; | ||||
worker.Name = string.Format("{0} GUI Thread", nameof(InteractiveListener)); | ||||
worker.Start(); | ||||
return p; | ||||
} | ||||
cin
|
r47 | 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() { | ||||
cin
|
r48 | m_exitPending = true; | ||
Resume(); | ||||
cin
|
r47 | m_syncGuiThread.Post(x => Application.ExitThread(), null); | ||
} | ||||
protected override void Dispose(bool disposing) { | ||||
if (disposing) { | ||||
Terminate(); | ||||
m_guiFinished.Join(); | ||||
} | ||||
base.Dispose(disposing); | ||||
} | ||||
cin
|
r48 | |||
cin
|
r134 | public override void Write(LogEventArgs args, object entry) { | ||
cin
|
r48 | var item = new TraceViewItem { | ||
cin
|
r134 | Indent = args.Operation.Level, | ||
Message = entry.ToString(), | ||||
cin
|
r92 | Thread = args.ThreadId, | ||
cin
|
r204 | Channel = args.Channel.ToString(), | ||
cin
|
r214 | Timestamp = Environment.TickCount, | ||
TimeDelta = args.OperationTimeOffset | ||||
cin
|
r48 | }; | ||
Enqueue(item); | ||||
} | ||||
cin
|
r47 | } | ||
} | ||||