changeset 92:4c0e5ef99986 v2

rewritten tracing
author cin
date Wed, 22 Oct 2014 18:37:56 +0400
parents cdaaf4792c22
children dc4942d09e74
files Implab.Diagnostics.Interactive/InteractiveListener.cs Implab/Diagnostics/ConsoleTraceListener.cs Implab/Diagnostics/Extensions.cs Implab/Diagnostics/IEventTextFormatter.cs Implab/Diagnostics/LogChannel.cs Implab/Diagnostics/LogEventArgs.cs Implab/Diagnostics/LogEventArgsT.cs Implab/Diagnostics/LogicalOperation.cs Implab/Diagnostics/OperationContext.cs Implab/Diagnostics/TextFileListener.cs Implab/Diagnostics/TextListenerBase.cs Implab/Diagnostics/TraceContext.cs Implab/Diagnostics/TraceLog.cs Implab/Implab.csproj Implab/Parallels/ArrayTraits.cs Implab/Parallels/AsyncPool.cs Implab/Parallels/DispatchPool.cs Implab/Parallels/WorkerPool.cs
diffstat 18 files changed, 268 insertions(+), 336 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Diagnostics.Interactive/InteractiveListener.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab.Diagnostics.Interactive/InteractiveListener.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -107,11 +107,11 @@
             base.Dispose(disposing);
         }
 
-        protected override void WriteEntry(TraceContext context, EventText text, string channel) {
+        protected override void WriteEntry(LogEventArgs args, EventText text, string channel) {
             var item = new TraceViewItem {
                 Indent = text.indent,
                 Message = text.content,
-                Thread = context.ThreadId,
+                Thread = args.ThreadId,
                 Channel = channel,
                 Timestamp = Environment.TickCount
             };
--- a/Implab/Diagnostics/ConsoleTraceListener.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/ConsoleTraceListener.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -18,16 +18,16 @@
 
         }
 
-        protected override void WriteEntry(TraceContext context, EventText text, string channel) {
+        protected override void WriteEntry(LogEventArgs args, EventText text, string channel) {
             var msg = new StringBuilder();
 
             for (int i = 0; i < text.indent; i++)
                 msg.Append("  ");
-            msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
+            msg.AppendFormat("[{0}]:{1}: {2}", args.ThreadId, channel, text.content);
 
             lock (_consoleLock) {
-                Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
-                Console.WriteLine(msg.ToString());
+                Console.ForegroundColor = (ConsoleColor)(args.ThreadId % 15 + 1);
+                Console.WriteLine(msg);
             }
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Diagnostics/Extensions.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -0,0 +1,15 @@
+namespace Implab.Diagnostics {
+    public static class Extensions {
+        public static IPromise<T> EndLogicalOperation<T>(this IPromise<T> promise) {
+            Safe.ArgumentNotNull(promise, "promise");
+            var op = TraceContext.Instance.DetachLogicalOperation();
+
+            return promise.Anyway(() => {
+                TraceContext.Instance.EnterLogicalOperation(op,true);
+                TraceLog.EndLogicalOperation();
+                TraceContext.Instance.Leave();
+            });
+        }
+    }
+}
+
--- a/Implab/Diagnostics/IEventTextFormatter.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/IEventTextFormatter.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -1,10 +1,5 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Implab.Diagnostics {
+namespace Implab.Diagnostics {
     public interface IEventTextFormatter<in TEvent> {
-        EventText Format(TraceContext context, TEvent data);
+        EventText Format(LogEventArgs args, TEvent data);
     }
 }
--- a/Implab/Diagnostics/LogChannel.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/LogChannel.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -27,8 +27,8 @@
         /// <summary>
         /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
         /// </summary>
-        public event EventHandler<ValueEventArgs<TEvent>> Events;
-        
+        public event EventHandler<LogEventArgs<TEvent>> Events;
+
         /// <summary>
         /// Имя канала, полезно для отображения в журнале
         /// </summary>
@@ -63,19 +63,18 @@
         /// </remarks>
         public void LogEvent(TEvent data) {
             var t = Events;
-            if (t!= null)
-                t(TraceContext.Current,new ValueEventArgs<TEvent>(data));
-        }
-
-        /// <summary>
-        /// Отправляет запись журнала через канал подписчикам.
-        /// </summary>
-        /// <param name="data">Запись журнала.</param>
-        /// <param name="context">Контекст трассировки от которого рассылается сообщение/</param>
-        public void LogEvent(TraceContext context,TEvent data) {
-            var t = Events;
-            if (t != null)
-                t(context, new ValueEventArgs<TEvent>(data));
+            if (t != null) {
+                var traceContext = TraceContext.Instance;
+                t(
+                    this,
+                    new LogEventArgs<TEvent>(
+                        data,
+                        traceContext.ThreadId,
+                        traceContext.CurrentOperation,
+                        traceContext.CurrentOperation.Duration
+                    )
+                );
+            }
         }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Diagnostics/LogEventArgs.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -0,0 +1,24 @@
+using System;
+
+namespace Implab.Diagnostics {
+    public class LogEventArgs : EventArgs {
+        public int ThreadId {
+            get;
+            private set;
+        }
+        public LogicalOperation Operation {
+            get;
+            private set;
+        }
+        public int OperationTimeOffset {
+            get;
+            private set;
+        }
+        public LogEventArgs(int threadId, LogicalOperation operation, int timeOffset) {
+            ThreadId = threadId;
+            Operation = operation;
+            OperationTimeOffset = timeOffset;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Diagnostics/LogEventArgsT.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -0,0 +1,13 @@
+namespace Implab.Diagnostics {
+    public class LogEventArgs<TEvent> : LogEventArgs {
+        public TEvent Value {
+            get;
+            private set;
+        }
+
+        public LogEventArgs(TEvent value, int threadId, LogicalOperation operation, int timeOffset) : base(threadId, operation, timeOffset) {
+            Value = value;
+        }
+    }
+}
+
--- a/Implab/Diagnostics/LogicalOperation.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/LogicalOperation.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -1,11 +1,9 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace Implab.Diagnostics {
     public class LogicalOperation {
+        public static readonly LogicalOperation EMPTY = new LogicalOperation("__EMPTY__", null);
+
         readonly LogicalOperation m_parent;
         readonly string m_name;
         readonly int m_level;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Diagnostics/OperationContext.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -0,0 +1,49 @@
+namespace Implab.Diagnostics {
+    struct OperationContext {
+        readonly LogicalOperation m_initial;
+        public readonly static OperationContext EMPTY = new OperationContext(LogicalOperation.EMPTY, false);
+        LogicalOperation m_current;
+        readonly bool m_ownership;
+
+        public OperationContext(LogicalOperation operation, bool ownership) {
+            Safe.ArgumentNotNull(operation, "operation");
+
+            m_initial = operation;
+            m_current = operation;
+            m_ownership = ownership;
+        }
+
+        public LogicalOperation CurrentOperation {
+            get { return m_current; }
+        }
+
+        public void BeginLogicalOperation(string name) {
+            m_current = new LogicalOperation(name, m_current);
+        }
+
+        public LogicalOperation DetachLogicalOperation() {
+            var detached = m_current;
+            if (m_current != LogicalOperation.EMPTY) {
+                if (m_current != m_initial)
+                    m_current = m_current.Parent;
+                else if (m_ownership)
+                    m_current = LogicalOperation.EMPTY;
+                else
+                    detached = LogicalOperation.EMPTY;
+            }
+            TraceLog.TraceWarning("EndLogicalOperation can't be applied in the current context");
+            return detached;
+        }
+
+        public void EndLogicalOperation() {
+            if (m_current != m_initial) {
+                m_current = m_current.Parent;
+            } else if (m_current != null && m_ownership) {
+                m_current = null;
+            } else {
+                TraceLog.TraceWarning("EndLogicalOperation can't be applied in the current context");
+            }
+        }
+    }
+}
+
--- a/Implab/Diagnostics/TextFileListener.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/TextFileListener.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -1,7 +1,5 @@
 using System;
-using System.Collections.Generic;
 using System.IO;
-using System.Linq;
 using System.Text;
 
 namespace Implab.Diagnostics {
@@ -16,16 +14,16 @@
             Register(this);
         }
 
-        protected override void WriteEntry(TraceContext context, EventText text, string channel) {
+        protected override void WriteEntry(LogEventArgs args, EventText text, string channel) {
             var msg = new StringBuilder();
             for (int i = 0; i < text.indent; i++)
                 msg.Append("  ");
-            msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
+            msg.AppendFormat("[{0}]:{1}: {2}", args.ThreadId, channel, text.content);
 
             lock (m_textWriter) {
                 if (!IsDisposed) {
                     // тут гарантировано еще не освобожден m_textWriter
-                    m_textWriter.WriteLine(msg.ToString());
+                    m_textWriter.WriteLine(msg);
                     m_textWriter.Flush();
                 }
             }
--- a/Implab/Diagnostics/TextListenerBase.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/TextListenerBase.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -13,7 +13,7 @@
         protected TextListenerBase(bool global) {
             Register(this);
             if (!global) {
-                m_boundOperation = TraceContext.Current.CurrentOperation;
+                m_boundOperation = TraceContext.Instance.CurrentOperation;
                 m_baseIndent = Math.Max(0, m_boundOperation.Level - 1);
             }
         }
@@ -38,13 +38,12 @@
                 var formatter = GetService<IEventTextFormatter<TEvent>>();
                 var channelName = channel.Name;
 
-                EventHandler<ValueEventArgs<TEvent>> handler = (sender, args) => {
-                    TraceContext context = (TraceContext)sender;
-                    var text = formatter.Format(context, args.Value);
+                EventHandler<LogEventArgs<TEvent>> handler = (sender, args) => {
+                    var text = formatter.Format(args, args.Value);
                     text.indent -= m_baseIndent;
 
-                    if (IsRelated(context.CurrentOperation))
-                        WriteEntry(context, text, channelName);
+                    if (IsRelated(args.Operation))
+                        WriteEntry(args, text, channelName);
                 };
 
                 if (m_subscriptions.ContainsKey(channel))
@@ -97,19 +96,19 @@
         /// Данный метод может вызваться из разных потоков одновременно. Возможна ситуация, когда
         /// данный метод вызывается уже после освобождения ообъекта методом <see cref="Dispose()"/>.
         /// </remarks>
-        /// <param name="context">Контекст трассировки.</param>
         /// <param name="text">Текст сообщения.</param>
-        protected abstract void WriteEntry(TraceContext context, EventText text, string channel);
+        /// <param name = "channel"></param>
+        protected abstract void WriteEntry(LogEventArgs args, EventText text, string channel);
         
-        public EventText Format(TraceContext context, object data) {
+        public EventText Format(LogEventArgs args, object data) {
             return new EventText {
-                indent = context.CurrentOperation.Level,
+                indent = args.Operation.Level,
                 content = data.ToString()
             };
         }
 
-        public EventText Format(TraceContext context, TraceEvent data) {
-            var level = context.CurrentOperation.Level;
+        public EventText Format(LogEventArgs args, TraceEvent data) {
+            var level = args.Operation.Level;
             if (data.EventType == TraceEventType.OperationCompleted || data.EventType == TraceEventType.OperationStarted)
                 level--;
 
--- a/Implab/Diagnostics/TraceContext.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/TraceContext.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -1,238 +1,79 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Implab.Diagnostics {
-    /// <summary>
-    /// Контекст трассировки, привязывается к потоку и содержит в себе информацию о стеке логических операций.
-    /// </summary>
-    /// <remarks>
-    /// Контекст трассировки передается слушателям событий для определения места, где возникло событие.
-    /// </remarks>
-    public class TraceContext {
-        LogicalOperation m_currentOperation;
-        readonly LogicalOperation m_bound;
-        readonly int m_threadId;
-
-        [ThreadStatic]
-        static TraceContext _current;
-
-        /// <summary>
-        /// Текущий контекст трассировки для потока, создается астоматически при первом обращении.
-        /// </summary>
-        public static TraceContext Current {
-            get {
-                if (_current == null) {
-                    _current = new TraceContext();
-                    _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
-                }
-                return _current;
-            }
-        }
-
-        TraceContext(TraceContext context)
-            : this(context, false) {
-        }
-
-        TraceContext(TraceContext context, bool attach) {
-            if (context == null)
-                throw new ArgumentNullException("context");
-
-            m_currentOperation = context.CurrentOperation;
-            m_bound = attach ? context.BoundOperation : context.CurrentOperation;
-            m_threadId = Thread.CurrentThread.ManagedThreadId;
-        }
-
-        TraceContext() {
-            m_currentOperation = new LogicalOperation();
-            m_bound = m_currentOperation;
-            m_threadId = Thread.CurrentThread.ManagedThreadId;
-        }
-
-        /// <summary>
-        /// При необходимости копирует состояние контекста трассивровки в текущий поток.
-        /// </summary>
-        /// <param name="from">Исходный контекст трассировки, который передается.</param>
-        /// <remarks>
-        /// <para>
-        /// Копирование происходит за счет создания нового контекста трассировки и заполнением его
-        /// состояния из переданного контекста. При этом копируется стек операций, однако в новом
-        /// контексте ранее начатые логические операции не могут быть завершены.
-        /// </para>
-        /// <para>
-        /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
-        /// </para>
-        /// </remarks>
-        public static void Fork(TraceContext from) {
-            if (_current == from)
-                return;
-            if (from != null) {
-                var context = new TraceContext(from);
-                context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
-                _current = context;
-            } else {
-                _current = new TraceContext();
-            }
-        }
-
-        /// <summary>
-        /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
-        /// логические операции в указанном контексте.
-        /// </summary>
-        /// <param name="source"></param>
-        public static void Attach(TraceContext source) {
-            if (_current == source)
-                return;
-            if (source != null) {
-                var context = new TraceContext(source, true);
-                context.LogEvent(TraceEventType.Attach, "[{0}]-->[{1}]", source.ThreadId, context.ThreadId);
-                _current = context;
-            } else {
-                _current = new TraceContext();
-            }
-        }
-
-        /// <summary>
-        /// Отсоединяет текущий контекст трассировки от потока, для дальнейшей его передачи другому потоку
-        /// <see cref="Attach(TraceContext)"/>.
-        /// </summary>
-        /// <returns>Контекст трассировки потока</returns>
-        /// <remarks>
-        /// После отсоединения контекста трассировки от потока, при первом обращении к трассировке в этом
-        /// потоке будет создан новый контекст.
-        /// </remarks>
-        public static TraceContext Detach() {
-            var context = Current;
-            context.LogEvent(TraceEventType.Detach, null);
-            _current = null;
-            return context;
-        }
-
-        /// <summary>
-        /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Fork(TraceContext)"/>
-        /// </summary>
-        /// <returns>Копия текущего контекста трассировки.</returns>
-        public static TraceContext Snapshot() {
-            return _current == null ? new TraceContext() : new TraceContext(_current,false);
-        }
-
-        /// <summary>
-        /// Выполняет переданное действие в указанном контексте трассировки, по окончании восстанавливает предыдущий контекст трассировки потока.
-        /// </summary>
-        /// <param name="action"></param>
-        public void Invoke(Action action) {
-            if (action == null)
-                throw new ArgumentNullException("action");
-            var old = _current;
-            Fork(this);
-            try {
-                action();
-            } finally {
-                if(_current != null)
-                    _current.EndAllOperations();
-                _current = old;
-            }
-        }
-
-        /// <summary>
-        /// Текущая логическая операция.
-        /// </summary>
-        public LogicalOperation CurrentOperation {
-            get {
-                return m_currentOperation;
-            }
-        }
-
-        /// <summary>
-        /// Операция ниже которой нельзя опускаться в стеке логических операций, т.е. она не может быть завершена в текущем контексте.
-        /// </summary>
-        public LogicalOperation BoundOperation {
-            get {
-                return m_bound;
-            }
-        }
-
-        /// <summary>
-        /// Поток, в котором создан контекст трассировки.
-        /// </summary>
-        public int ThreadId {
-            get {
-                return m_threadId;
-            }
-        }
-
-        /// <summary>
-        /// Начинает безымянную логическую операцию.
-        /// </summary>
-        public void StartLogicalOperation() {
-            StartLogicalOperation(null);
-        }
-
-        /// <summary>
-        /// Начинает логическую операцию с указанным именем. Созданная операция будет добвалена в стек логических операций контекста, затем будет создано соответсвующее событие.
-        /// </summary>
-        /// <param name="name">Имя начинаемой операции.</param>
-        public void StartLogicalOperation(string name) {
-            m_currentOperation = new LogicalOperation(name, m_currentOperation);
-            LogEvent(TraceEventType.OperationStarted, name);
-        }
-
-        /// <summary>
-        /// Заканчивает логическую операцию начатую в текущем контексте. Операции, начатые в других контекстах не могут быть закончены в текущем контексте.
-        /// </summary>
-        /// <remarks>
-        /// При вызове данного метода создается событие журнала трассировки, либо о завершении операции, либо об ошибки, поскольку данная операция
-        /// начата в другом контексте.
-        /// </remarks>
-        public void EndLogicalOperation() {
-            if (m_bound == m_currentOperation) {
-                LogEvent(TraceEventType.Error, "Trying to end the operation which isn't belongs to current trace");
-            } else {
-                var op = m_currentOperation;
-                LogEvent(TraceEventType.OperationCompleted, "{0} {1} ms", op.Name, op.Duration);
-                m_currentOperation = m_currentOperation.Parent;
-            }
-        }
-
-        /// <summary>
-        /// Создает копию контекста и возвращается на предыдущую операцию в текущем контексте, это позволяет начать операцию в одном потоке, а завершить - в другом.
-        /// </summary>
-        /// <returns>Контекст трассировки, который можно присоединить к другому потоку.</returns>
-        public TraceContext DetachLogicalOperation() {
-            if (m_bound == m_currentOperation) {
-                return new TraceContext();
-            } else {
-                var detached = new TraceContext(this, true);
-                m_currentOperation = m_currentOperation.Parent;
-                return detached;
-            }
-        }
-
-        public void BindLogicalOperationToPromise(IPromise promise) {
-            Safe.ArgumentNotNull(promise, "promise");
-
-            var ctx = DetachLogicalOperation();
-            promise.Anyway(() => {
-                var old = _current;
-                TraceContext.Attach(ctx);
-                TraceContext.Current.EndLogicalOperation();
-                _current = old;
-            });
-        }
-
-        /// <summary>
-        /// Заврешает все начатые в этом контексте операции
-        /// </summary>
-        public void EndAllOperations() {
-            while (m_bound != m_currentOperation)
-                EndLogicalOperation();
-        }
-
-        void LogEvent(TraceEventType type, string format, params object[] args) {
-            LogChannel<TraceEvent>.Default.LogEvent(this, TraceEvent.Create(type, format, args));
-        }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Implab.Diagnostics {
+    /// <summary>
+    /// Trace context is bound to the specific thread, each thread has it's own ThreadContext.
+    /// </summary>
+    /// <remarks>
+    /// ThreadContext manages relations between logical operations and threads.
+    /// </remarks>
+    public class TraceContext {
+
+        [ThreadStatic]
+        static TraceContext _instance;
+
+        OperationContext m_current = OperationContext.EMPTY;
+        readonly Stack<OperationContext> m_stack = new Stack<OperationContext>();
+        readonly int m_threadId;
+
+        public static TraceContext Instance {
+            get {
+                if (_instance == null)
+                    _instance = new TraceContext();
+                return _instance;
+            }
+        }
+
+        public TraceContext() {
+            m_threadId = Thread.CurrentThread.ManagedThreadId;
+        }
+
+        public int ThreadId {
+            get { return m_threadId; }
+        }
+
+        public LogicalOperation CurrentOperation {
+            get {
+                return m_current.CurrentOperation;
+            }
+        }
+
+        public void EnterLogicalOperation(LogicalOperation operation, bool takeOwnership) {
+            // TODO Emit event
+            m_stack.Push(m_current);
+            m_current = new OperationContext(operation, takeOwnership);
+        }
+
+        public void StartLogicalOperation(string name) {
+            m_current.BeginLogicalOperation(name);
+        }
+
+        public void StartLogicalOperation() {
+            // TODO Emit Event
+            m_current.BeginLogicalOperation(String.Empty);
+        }
+
+        public void EndLogicalOperation() {
+            // TODO Emit event
+            m_current.EndLogicalOperation();
+        }
+
+        public LogicalOperation DetachLogicalOperation() {
+            // TODO Emit event
+            return m_current.DetachLogicalOperation();
+        }
+
+        public void Leave() {
+            // TODO Emit event
+            if (m_stack.Count > 0)
+                m_current = m_stack.Pop();
+            else {
+                TraceLog.TraceWarning("Attemtp to leave the last operation context");
+                m_current = OperationContext.EMPTY;
+            }
+        }
+    }
+}
+
--- a/Implab/Diagnostics/TraceLog.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Diagnostics/TraceLog.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -14,22 +14,17 @@
     public static class TraceLog {
         [Conditional("TRACE")]
         public static void StartLogicalOperation() {
-            TraceContext.Current.StartLogicalOperation();
+            TraceContext.Instance.StartLogicalOperation();
         }
 
         [Conditional("TRACE")]
         public static void StartLogicalOperation(string name) {
-            TraceContext.Current.StartLogicalOperation(name);
+            TraceContext.Instance.StartLogicalOperation(name);
         }
 
         [Conditional("TRACE")]
         public static void EndLogicalOperation() {
-            TraceContext.Current.EndLogicalOperation();
-        }
-
-        [Conditional("TRACE")]
-        public static void BindLogicalOperationToPromise(IPromise promise) {
-            TraceContext.Current.BindLogicalOperationToPromise(promise);
+            TraceContext.Instance.EndLogicalOperation();
         }
 
         [Conditional("TRACE")]
--- a/Implab/Implab.csproj	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Implab.csproj	Wed Oct 22 18:37:56 2014 +0400
@@ -80,7 +80,6 @@
     <Compile Include="Diagnostics\TextFileListener.cs" />
     <Compile Include="Diagnostics\TextListenerBase.cs" />
     <Compile Include="Diagnostics\TraceLog.cs" />
-    <Compile Include="Diagnostics\TraceContext.cs" />
     <Compile Include="Diagnostics\TraceEvent.cs" />
     <Compile Include="Diagnostics\TraceEventType.cs" />
     <Compile Include="Disposable.cs" />
@@ -141,6 +140,11 @@
     <Compile Include="TransientPromiseException.cs" />
     <Compile Include="SyncContextPromise.cs" />
     <Compile Include="ObjectPool.cs" />
+    <Compile Include="Diagnostics\OperationContext.cs" />
+    <Compile Include="Diagnostics\TraceContext.cs" />
+    <Compile Include="Diagnostics\LogEventArgs.cs" />
+    <Compile Include="Diagnostics\LogEventArgsT.cs" />
+    <Compile Include="Diagnostics\Extensions.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup />
--- a/Implab/Parallels/ArrayTraits.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Parallels/ArrayTraits.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -12,7 +12,7 @@
             readonly Action<TSrc> m_action;
             readonly TSrc[] m_source;
             readonly Promise<int> m_promise = new Promise<int>();
-            readonly TraceContext m_traceContext;
+            readonly LogicalOperation m_logicalOperation;
 
             int m_pending;
             int m_next;
@@ -23,7 +23,7 @@
                 Debug.Assert(source != null);
                 Debug.Assert(action != null);
 
-                m_traceContext = TraceContext.Snapshot();
+                m_logicalOperation = TraceContext.Instance.CurrentOperation;
                 m_next = 0;
                 m_source = source;
                 m_pending = source.Length;
@@ -41,8 +41,12 @@
             }
 
             protected override void Worker() {
-                TraceContext.Fork(m_traceContext);
-                base.Worker();
+                TraceContext.Instance.EnterLogicalOperation(m_logicalOperation, false);
+                try {
+                    base.Worker();
+                } finally {
+                    TraceContext.Instance.Leave();
+                }
             }
 
             protected override bool TryDequeue(out int unit) {
@@ -67,7 +71,7 @@
             readonly TSrc[] m_source;
             readonly TDst[] m_dest;
             readonly Promise<TDst[]> m_promise = new Promise<TDst[]>();
-            readonly TraceContext m_traceContext;
+            readonly LogicalOperation m_logicalOperation;
 
             int m_pending;
             int m_next;
@@ -83,7 +87,7 @@
                 m_dest = new TDst[source.Length];
                 m_pending = source.Length;
                 m_transform = transform;
-                m_traceContext = TraceContext.Snapshot();
+                m_logicalOperation = TraceContext.Instance.CurrentOperation;
 
                 m_promise.Anyway(Dispose);
 
@@ -97,13 +101,17 @@
             }
 
             protected override void Worker() {
-                TraceContext.Fork(m_traceContext);
-                base.Worker();
+                TraceContext.Instance.EnterLogicalOperation(m_logicalOperation,false);
+                try {
+                    base.Worker();
+                } finally {
+                    TraceContext.Instance.Leave();
+                }
             }
 
             protected override bool TryDequeue(out int unit) {
                 unit = Interlocked.Increment(ref m_next) - 1;
-                return unit >= m_source.Length ? false : true;
+                return unit < m_source.Length;
             }
 
             protected override void InvokeUnit(int unit) {
--- a/Implab/Parallels/AsyncPool.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Parallels/AsyncPool.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -14,15 +14,17 @@
 
 		public static IPromise<T> Invoke<T>(Func<T> func) {
 			var p = new Promise<T>();
-            var caller = TraceContext.Snapshot();
+            var caller = TraceContext.Instance.CurrentOperation;
 
 			ThreadPool.QueueUserWorkItem(param => {
-                TraceContext.Fork(caller);
+                TraceContext.Instance.EnterLogicalOperation(caller,false);
 				try {
 					p.Resolve(func());
 				} catch(Exception e) {
 					p.Reject(e);
-				}
+                } finally {
+                    TraceContext.Instance.Leave();
+                }
 			});
 
 			return p;
@@ -31,14 +33,16 @@
         public static IPromise<T> InvokeNewThread<T>(Func<T> func) {
             var p = new Promise<T>();
 
-            var caller = TraceContext.Snapshot();
+            var caller = TraceContext.Instance.CurrentOperation;
 
             var worker = new Thread(() => {
-                TraceContext.Fork(caller);
+                TraceContext.Instance.EnterLogicalOperation(caller,false);
                 try {
                     p.Resolve(func());
                 } catch (Exception e) {
                     p.Reject(e);
+                } finally {
+                    TraceContext.Instance.Leave();
                 }
             });
             worker.IsBackground = true;
@@ -51,15 +55,17 @@
         public static IPromise InvokeNewThread(Action func) {
             var p = new Promise<object>();
 
-            var caller = TraceContext.Snapshot();
+            var caller = TraceContext.Instance.CurrentOperation;
 
             var worker = new Thread(() => {
-                TraceContext.Fork(caller);
+                TraceContext.Instance.EnterLogicalOperation(caller,false);
                 try {
                     func();
                     p.Resolve();
                 } catch (Exception e) {
                     p.Reject(e);
+                } finally {
+                    TraceContext.Instance.Leave();
                 }
             });
             worker.IsBackground = true;
--- a/Implab/Parallels/DispatchPool.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Parallels/DispatchPool.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -1,9 +1,5 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 using System.Threading;
-using System.Diagnostics;
 
 namespace Implab.Parallels {
     public abstract class DispatchPool<TUnit> : IDisposable {
@@ -150,14 +146,13 @@
         protected bool StartWorker() {
             if (AllocateThreadSlot()) {
                 // slot successfully allocated
-                var worker = new Thread(this.Worker);
+                var worker = new Thread(Worker);
                 worker.IsBackground = true;
                 worker.Start();
 
                 return true;
-            } else {
-                return false;
             }
+            return false;
         }
 
         protected abstract void InvokeUnit(TUnit unit);
--- a/Implab/Parallels/WorkerPool.cs	Tue Oct 14 09:30:45 2014 +0400
+++ b/Implab/Parallels/WorkerPool.cs	Wed Oct 22 18:37:56 2014 +0400
@@ -1,7 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 using System.Threading;
 using System.Diagnostics;
 using Implab.Diagnostics;
@@ -12,29 +9,24 @@
         MTQueue<Action> m_queue = new MTQueue<Action>();
         int m_queueLength = 0;
         readonly int m_threshold = 1;
-        int m_workers = 0;
 
         public WorkerPool(int minThreads, int maxThreads, int threshold)
             : base(minThreads, maxThreads) {
             m_threshold = threshold;
-            m_workers = minThreads;
             InitPool();
         }
 
         public WorkerPool(int minThreads, int maxThreads) :
             base(minThreads, maxThreads) {
-            m_workers = minThreads;
             InitPool();
         }
 
         public WorkerPool(int threads)
             : base(threads) {
-            m_workers = threads;
             InitPool();
         }
 
-        public WorkerPool()
-            : base() {
+        public WorkerPool() {
             InitPool();
         }
 
@@ -46,16 +38,17 @@
 
             var promise = new Promise<T>();
 
-            var caller = TraceContext.Snapshot();
+            var lop = TraceContext.Instance.CurrentOperation;
 
             EnqueueTask(delegate() {
-                caller.Invoke(delegate() {
-                    try {
-                        promise.Resolve(task());
-                    } catch (Exception e) {
-                        promise.Reject(e);
-                    }
-                });
+                TraceContext.Instance.EnterLogicalOperation(lop, false);
+                try {
+                    promise.Resolve(task());
+                } catch (Exception e) {
+                    promise.Reject(e);
+                } finally {
+                    TraceContext.Instance.Leave();
+                }
             });
 
             return promise;