changeset 50:f8cbe84cfdb1

Слияние с interactive logger
author cin
date Fri, 18 Apr 2014 12:37:48 +0400
parents e5ec543feee3 (current diff) 6f335e65b75f (diff)
children 2c332a9c64c0
files Implab/ManagedPromise.cs
diffstat 21 files changed, 784 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Apr 16 19:02:58 2014 +0400
+++ b/.hgignore	Fri Apr 18 12:37:48 2014 +0400
@@ -11,3 +11,5 @@
 Implab.Fx.Test/bin/
 Implab.Fx.Test/obj/
 _ReSharper.Implab/
+Implab.Diagnostics.Interactive/bin/
+Implab.Diagnostics.Interactive/obj/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/Implab.Diagnostics.Interactive.csproj	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{1DB7DB0C-8AA9-484B-A681-33AE94038391}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Implab.Diagnostics.Interactive</RootNamespace>
+    <AssemblyName>Implab.Diagnostics.Interactive</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="InteractiveListener.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="TextStyle.cs" />
+    <Compile Include="TraceForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="TraceForm.Designer.cs">
+      <DependentUpon>TraceForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="TraceViewItem.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Implab\Implab.csproj">
+      <Project>{f550f1f8-8746-4ad0-9614-855f4c4b7f05}</Project>
+      <Name>Implab</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="TraceForm.resx">
+      <DependentUpon>TraceForm.cs</DependentUpon>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Properties\DataSources\TraceViewItem.datasource" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/InteractiveListener.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,122 @@
+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 IPromiseBase m_guiFinished;
+        readonly IPromiseBase 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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/Properties/AssemblyInfo.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Implab.Diagnostics.Interactive")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Implab.Diagnostics.Interactive")]
+[assembly: AssemblyCopyright("Copyright ©  2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1c156c51-4884-43b2-a823-d86313872e82")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/Properties/DataSources/TraceViewItem.datasource	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    This file is automatically generated by Visual Studio .Net. It is 
+    used to store generic object data source configuration information.  
+    Renaming the file extension or editing the content of this file may   
+    cause the file to be unrecognizable by the program.
+-->
+<GenericObjectDataSource DisplayName="TraceViewItem" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
+   <TypeInfo>Implab.Diagnostics.Interactive.TraceViewItem, Implab.Diagnostics.Interactive</TypeInfo>
+</GenericObjectDataSource>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/TextStyle.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Implab.Diagnostics.Interactive {
+    class TextStyle {
+        public Color TextColor {
+            get;
+            set;
+        }
+        
+        public FontStyle FontStyle {
+            get;
+            set;
+        }
+
+        public int PaddingLeft {
+            get;
+            set;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/TraceForm.Designer.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,134 @@
+namespace Implab.Diagnostics.Interactive {
+    partial class TraceForm {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing) {
+            if (disposing && (components != null)) {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent() {
+            this.components = new System.ComponentModel.Container();
+            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
+            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
+            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
+            this.eventsDataGrid = new System.Windows.Forms.DataGridView();
+            this.traceViewItemBindingSource = new System.Windows.Forms.BindingSource(this.components);
+            this.threadDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.Channel = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.messageDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            ((System.ComponentModel.ISupportInitialize)(this.eventsDataGrid)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.traceViewItemBindingSource)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // eventsDataGrid
+            // 
+            this.eventsDataGrid.AllowUserToAddRows = false;
+            this.eventsDataGrid.AllowUserToDeleteRows = false;
+            this.eventsDataGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.eventsDataGrid.AutoGenerateColumns = false;
+            this.eventsDataGrid.BackgroundColor = System.Drawing.SystemColors.Window;
+            dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+            dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
+            dataGridViewCellStyle1.Font = new System.Drawing.Font("Lucida Console", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
+            dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
+            dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+            dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+            dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
+            this.eventsDataGrid.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
+            this.eventsDataGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+            this.eventsDataGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+            this.threadDataGridViewTextBoxColumn,
+            this.Channel,
+            this.messageDataGridViewTextBoxColumn});
+            this.eventsDataGrid.DataSource = this.traceViewItemBindingSource;
+            dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
+            dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Window;
+            dataGridViewCellStyle3.Font = new System.Drawing.Font("Lucida Console", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
+            dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.ControlText;
+            dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;
+            dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
+            dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+            this.eventsDataGrid.DefaultCellStyle = dataGridViewCellStyle3;
+            this.eventsDataGrid.Location = new System.Drawing.Point(12, 12);
+            this.eventsDataGrid.Name = "eventsDataGrid";
+            this.eventsDataGrid.ReadOnly = true;
+            this.eventsDataGrid.RowHeadersWidth = 17;
+            this.eventsDataGrid.RowTemplate.Resizable = System.Windows.Forms.DataGridViewTriState.False;
+            this.eventsDataGrid.Size = new System.Drawing.Size(939, 480);
+            this.eventsDataGrid.TabIndex = 1;
+            this.eventsDataGrid.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.eventsDataGrid_CellFormatting);
+            // 
+            // traceViewItemBindingSource
+            // 
+            this.traceViewItemBindingSource.DataSource = typeof(Implab.Diagnostics.Interactive.TraceViewItem);
+            // 
+            // threadDataGridViewTextBoxColumn
+            // 
+            this.threadDataGridViewTextBoxColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCellsExceptHeader;
+            this.threadDataGridViewTextBoxColumn.DataPropertyName = "Thread";
+            this.threadDataGridViewTextBoxColumn.HeaderText = "TID";
+            this.threadDataGridViewTextBoxColumn.Name = "threadDataGridViewTextBoxColumn";
+            this.threadDataGridViewTextBoxColumn.ReadOnly = true;
+            this.threadDataGridViewTextBoxColumn.Width = 5;
+            // 
+            // Channel
+            // 
+            this.Channel.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.DisplayedCells;
+            this.Channel.DataPropertyName = "Channel";
+            this.Channel.HeaderText = "Channel";
+            this.Channel.Name = "Channel";
+            this.Channel.ReadOnly = true;
+            this.Channel.Width = 79;
+            // 
+            // messageDataGridViewTextBoxColumn
+            // 
+            this.messageDataGridViewTextBoxColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
+            this.messageDataGridViewTextBoxColumn.DataPropertyName = "FormattedMessage";
+            dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
+            this.messageDataGridViewTextBoxColumn.DefaultCellStyle = dataGridViewCellStyle2;
+            this.messageDataGridViewTextBoxColumn.HeaderText = "Message";
+            this.messageDataGridViewTextBoxColumn.Name = "messageDataGridViewTextBoxColumn";
+            this.messageDataGridViewTextBoxColumn.ReadOnly = true;
+            // 
+            // TraceForm
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(963, 504);
+            this.Controls.Add(this.eventsDataGrid);
+            this.Name = "TraceForm";
+            this.Text = "TraceForm";
+            ((System.ComponentModel.ISupportInitialize)(this.eventsDataGrid)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.traceViewItemBindingSource)).EndInit();
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.DataGridView eventsDataGrid;
+        private System.Windows.Forms.BindingSource traceViewItemBindingSource;
+        private System.Windows.Forms.DataGridViewTextBoxColumn threadDataGridViewTextBoxColumn;
+        private System.Windows.Forms.DataGridViewTextBoxColumn Channel;
+        private System.Windows.Forms.DataGridViewTextBoxColumn messageDataGridViewTextBoxColumn;
+
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/TraceForm.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Implab.Diagnostics.Interactive {
+    public partial class TraceForm : Form {
+        readonly Dictionary<int, Color> m_threadColors = new Dictionary<int,Color>();
+        readonly Random m_rand = new Random();
+
+        public event EventHandler PauseEvents;
+
+        public event EventHandler ResumeEvents;
+
+        public TraceForm() {
+            InitializeComponent();
+        }
+
+        protected override void OnFormClosing(FormClosingEventArgs e) {
+            base.OnFormClosing(e);
+            if (!e.Cancel && e.CloseReason == CloseReason.UserClosing) {
+                e.Cancel = true;
+                Hide();
+            }
+        }
+
+        public void AddTraceEvent(TraceViewItem item) {
+            traceViewItemBindingSource.Add(item);
+            eventsDataGrid.FirstDisplayedScrollingRowIndex = eventsDataGrid.RowCount - 1;
+        }
+
+        Color GetThreadColor(int thread) {
+            Color result;
+            if (!m_threadColors.TryGetValue(thread, out result)) {
+                result = Color.FromArgb(m_rand.Next(4)*64, m_rand.Next(4)*64, m_rand.Next(4)*64);
+                m_threadColors[thread] = result;
+            }
+            return result;
+        }
+
+        private void eventsDataGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) {
+            var data = (TraceViewItem)traceViewItemBindingSource[e.RowIndex];
+            if (e.ColumnIndex == messageDataGridViewTextBoxColumn.Index)
+                e.CellStyle.Padding = new Padding(data.Indent * 10,0,0,0);
+            e.CellStyle.ForeColor = GetThreadColor(data.Thread);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/TraceForm.resx	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="Channel.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <metadata name="traceViewItemBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Diagnostics.Interactive/TraceViewItem.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Implab.Diagnostics.Interactive {
+    public class TraceViewItem {
+        string m_formattedValue;
+
+        public string Message { get; set; }
+        public int Timestamp { get; set; }
+        public int Indent { get; set; }
+        public int Thread { get; set; }
+        public string Channel { get; set; }
+
+        public string FormattedMessage {
+            get {
+                if (m_formattedValue == null) {
+                    m_formattedValue = Message.Replace("\r",String.Empty).Replace("\n", " | ");
+                }
+                return m_formattedValue;
+            }
+        }
+    }
+}
--- a/Implab/Diagnostics/ConsoleTraceListener.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Diagnostics/ConsoleTraceListener.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -13,17 +13,17 @@
 
         }
 
-        public ConsoleTraceListener(bool local)
-            : base(local) {
+        public ConsoleTraceListener(bool global)
+            : base(global) {
 
         }
 
-        protected override void WriteEntry(TraceContext context, EventText text) {
+        protected override void WriteEntry(TraceContext context, EventText text, string channel) {
             var msg = new StringBuilder();
 
             for (int i = 0; i < text.indent; i++)
                 msg.Append("  ");
-            msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
+            msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
 
             lock (_consoleLock) {
                 Console.ForegroundColor = (ConsoleColor)(context.ThreadId % 15 + 1);
--- a/Implab/Diagnostics/LogChannel.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Diagnostics/LogChannel.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -4,23 +4,74 @@
 using System.Text;
 
 namespace Implab.Diagnostics {
+    /// <summary>
+    /// Канал, через который публикуются события журнала.
+    /// </summary>
+    /// <typeparam name="TEvent">Тип событий в канале</typeparam>
+    /// <remarks>
+    /// Событиями журнала могут быть любые типы, например строки, в которых будет передаваться
+    /// информация, или структуры с набором полей, описывающих  важность, текст и другую информацию.
+    /// </remarks>
     public class LogChannel<TEvent> {
         static LogChannel<TEvent> _default = new LogChannel<TEvent>();
 
+        /// <summary>
+        /// Канал по-умолчанию для событий типа <typeparam name="TEvent"/>.
+        /// </summary>
         public static LogChannel<TEvent> Default {
             get {
                 return _default;
             }
         }
 
+        /// <summary>
+        /// Событие появление новой записи в журнале, на это событие подписываются слушатели.
+        /// </summary>
         public event EventHandler<ValueEventArgs<TEvent>> Events;
+        
+        /// <summary>
+        /// Имя канала, полезно для отображения в журнале
+        /// </summary>
+        public string Name {
+            get;
+            private set;
+        }
 
+        /// <summary>
+        /// Создает журнал, имя типа событий назначается в качетве имени канала.
+        /// </summary>
+        public LogChannel()
+            : this(null) {
+        }
+
+        /// <summary>
+        /// Содает канал с указанным именем.
+        /// </summary>
+        /// <param name="name">Имя канала.</param>
+        public LogChannel(string name) {
+            if (String.IsNullOrEmpty(name))
+                name = typeof(TEvent).Name;
+            Name = name;
+        }
+
+        /// <summary>
+        /// Отправляет запись журнала через канал подписчикам.
+        /// </summary>
+        /// <param name="data">Запись журнала.</param>
+        /// <remarks>
+        /// Контекст трассировки от которого рассылается сообщение определяется автоматически из текущего потока.
+        /// </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)
--- a/Implab/Diagnostics/TextFileListener.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Diagnostics/TextFileListener.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -8,21 +8,23 @@
     public class TextFileListener: TextListenerBase {
         readonly TextWriter m_textWriter;
 
-        public TextFileListener(string fileName, bool local) : base(local) {
+        public TextFileListener(string fileName, bool global)
+            : base(global) {
             m_textWriter = File.CreateText(fileName);
 
             m_textWriter.WriteLine("LOG {0}", DateTime.Now);
             Register(this);
         }
 
-        protected override void WriteEntry(TraceContext context, EventText text) {
+        protected override void WriteEntry(TraceContext context, EventText text, string channel) {
             var msg = new StringBuilder();
             for (int i = 0; i < text.indent; i++)
                 msg.Append("  ");
-            msg.AppendFormat("[{0}]: {1}", context.ThreadId, text.content);
+            msg.AppendFormat("[{0}]:{1}: {2}", context.ThreadId, channel, text.content);
 
             lock (m_textWriter) {
                 if (!IsDisposed) {
+                    // тут гарантировано еще не освобожден m_textWriter
                     m_textWriter.WriteLine(msg.ToString());
                     m_textWriter.Flush();
                 }
@@ -33,6 +35,7 @@
         protected override void Dispose(bool disposing) {
             base.Dispose(disposing);
             if (disposing) {
+                // IsDisposed = true
                 lock (m_textWriter) {
                     Safe.Dispose(m_textWriter);
                 }
--- a/Implab/Diagnostics/TextListenerBase.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Diagnostics/TextListenerBase.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -10,9 +10,9 @@
         readonly LogicalOperation m_boundOperation;
         readonly int m_baseIndent;
 
-        protected TextListenerBase(bool local) {
+        protected TextListenerBase(bool global) {
             Register(this);
-            if (local) {
+            if (!global) {
                 m_boundOperation = TraceContext.Current.CurrentOperation;
                 m_baseIndent = Math.Max(0, m_boundOperation.Level - 1);
             }
@@ -36,6 +36,7 @@
                 AssertNotDisposed();
 
                 var formatter = GetService<IEventTextFormatter<TEvent>>();
+                var channelName = channel.Name;
 
                 EventHandler<ValueEventArgs<TEvent>> handler = (sender, args) => {
                     TraceContext context = (TraceContext)sender;
@@ -43,7 +44,7 @@
                     text.indent -= m_baseIndent;
 
                     if (IsRelated(context.CurrentOperation))
-                        WriteEntry(context, text);
+                        WriteEntry(context, text, channelName);
                 };
 
                 if (m_subscriptions.ContainsKey(channel))
@@ -89,7 +90,16 @@
             }
         }
 
-        protected abstract void WriteEntry(TraceContext context, EventText text);
+        /// <summary>
+        /// Вызывается для записи текста сообщения, в журнал.
+        /// </summary>
+        /// <remarks>
+        /// Данный метод может вызваться из разных потоков одновременно. Возможна ситуация, когда
+        /// данный метод вызывается уже после освобождения ообъекта методом <see cref="Dispose()"/>.
+        /// </remarks>
+        /// <param name="context">Контекст трассировки.</param>
+        /// <param name="text">Текст сообщения.</param>
+        protected abstract void WriteEntry(TraceContext context, EventText text, string channel);
         
         public EventText Format(TraceContext context, object data) {
             return new EventText {
@@ -110,10 +120,10 @@
         }
 
         protected override void Dispose(bool disposing) {
+            base.Dispose(disposing);
             if (disposing) {
                 UnsubscribeAll();
             }
-            base.Dispose(disposing);
         }
     }
 }
--- a/Implab/Diagnostics/TraceContext.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Diagnostics/TraceContext.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -25,18 +25,24 @@
         /// </summary>
         public static TraceContext Current {
             get {
-                if (_current == null)
+                if (_current == null) {
                     _current = new TraceContext();
+                    _current.LogEvent(TraceEventType.Created,"[{0}]", _current.ThreadId);
+                }
                 return _current;
             }
         }
 
-        TraceContext(TraceContext context) {
+        TraceContext(TraceContext context)
+            : this(context, false) {
+        }
+
+        TraceContext(TraceContext context, bool attach) {
             if (context == null)
                 throw new ArgumentNullException("context");
 
             m_currentOperation = context.CurrentOperation;
-            m_bound = context.CurrentOperation;
+            m_bound = attach ? context.BoundOperation : context.CurrentOperation;
             m_threadId = Thread.CurrentThread.ManagedThreadId;
         }
 
@@ -57,15 +63,15 @@
         /// контексте ранее начатые логические операции не могут быть завершены.
         /// </para>
         /// <para>
-        /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Transfer"/>.
+        /// Если передача состояния состоялась, то вызывается событие трассировки <see cref="TraceEventType.Fork"/>.
         /// </para>
         /// </remarks>
-        public static void Transfer(TraceContext from) {
+        public static void Fork(TraceContext from) {
             if (_current == from)
                 return;
             if (from != null) {
                 var context = new TraceContext(from);
-                context.LogEvent(TraceEventType.Transfer, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
+                context.LogEvent(TraceEventType.Fork, "[{0}]-->[{1}]",from.ThreadId, context.ThreadId);
                 _current = context;
             } else {
                 _current = new TraceContext();
@@ -73,7 +79,40 @@
         }
 
         /// <summary>
-        /// Создает постоянную копию текущего контекста, данную копию можно хранить и использовать для передачи через <see cref="Transfer(TraceContext)"/>
+        /// Задает текущему потоку указанный контекст, текущей поток может заканчивать ранее начатые
+        /// логические операции в указанном контексте.
+        /// </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() {
@@ -88,7 +127,7 @@
             if (action == null)
                 throw new ArgumentNullException("action");
             var old = _current;
-            Transfer(this);
+            Fork(this);
             try {
                 action();
             } finally {
--- a/Implab/Diagnostics/TraceEventType.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Diagnostics/TraceEventType.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -11,6 +11,9 @@
         Error,
         OperationStarted,
         OperationCompleted,
-        Transfer
+        Fork,
+        Attach,
+        Detach,
+        Created
     }
 }
--- a/Implab/Disposable.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Disposable.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -1,10 +1,14 @@
-using System;
+using Implab.Diagnostics;
+using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
 using System.Web;
 
 namespace Implab {
+    /// <summary>
+    /// Объект, поддерживающий освобождение ресурсов.
+    /// </summary>
     public class Disposable : IDisposable {
         
         bool m_disposed;
@@ -19,7 +23,16 @@
             if (m_disposed)
                 throw new ObjectDisposedException(this.ToString());
         }
-
+        /// <summary>
+        /// Переводит объект в состояние <c>Disposed</c> и вызывает событие <see cref="Disposed"/>
+        /// </summary>
+        /// <param name="disposing">Признак того, что нужно освободить ресурсы, иначе данный метод
+        /// вызван сборщиком мусора и нужно освобождать ТОЛЬКО неуправляемые ресурсы ТОЛЬКО этого
+        /// объекта.</param>
+        /// <remarks>
+        /// Данный метод осуществляет проверку того, что объект уже был освобожден, чтобы не вызывать
+        /// событие <see cref="Disposed"/>. Не поддерживает многопоточность.
+        /// </remarks>
         protected virtual void Dispose(bool disposing) {
             if (disposing && !m_disposed) {
                 m_disposed = true;
@@ -34,8 +47,11 @@
             GC.SuppressFinalize(this);
         }
 
+        /// <summary>
+        /// Записывает сообщение об утечке объекта.
+        /// </summary>
         protected virtual void ReportObjectLeaks() {
-            Trace.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this);
+            TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this);
         }
 
         ~Disposable() {
--- a/Implab/Implab.csproj	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Implab.csproj	Fri Apr 18 12:37:48 2014 +0400
@@ -52,7 +52,6 @@
     <Compile Include="IPromiseBase.cs" />
     <Compile Include="IServiceLocator.cs" />
     <Compile Include="ITaskController.cs" />
-    <Compile Include="ManagedPromise.cs" />
     <Compile Include="Parallels\DispatchPool.cs" />
     <Compile Include="Parallels\ArrayTraits.cs" />
     <Compile Include="Parallels\MTQueue.cs" />
--- a/Implab/ManagedPromise.cs	Wed Apr 16 19:02:58 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Implab {
-
-    /*public class ManagedPromise<T>: Promise<T>, ITaskController, IProgressNotifier {
-        
-    }*/
-}
--- a/Implab/Parallels/ArrayTraits.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Parallels/ArrayTraits.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -42,7 +42,7 @@
             }
 
             protected override void Worker() {
-                TraceContext.Transfer(m_traceContext);
+                TraceContext.Fork(m_traceContext);
                 base.Worker();
             }
 
@@ -99,7 +99,7 @@
             }
 
             protected override void Worker() {
-                TraceContext.Transfer(m_traceContext);
+                TraceContext.Fork(m_traceContext);
                 base.Worker();
             }
 
--- a/Implab/Parallels/AsyncPool.cs	Wed Apr 16 19:02:58 2014 +0400
+++ b/Implab/Parallels/AsyncPool.cs	Fri Apr 18 12:37:48 2014 +0400
@@ -12,12 +12,12 @@
 	/// </remarks>
 	public static class AsyncPool {
 
-		public static Promise<T> Invoke<T>(Func<T> func) {
+		public static IPromise<T> Invoke<T>(Func<T> func) {
 			var p = new Promise<T>();
             var caller = TraceContext.Snapshot();
 
 			ThreadPool.QueueUserWorkItem(param => {
-                TraceContext.Transfer(caller);
+                TraceContext.Fork(caller);
 				try {
 					p.Resolve(func());
 				} catch(Exception e) {
@@ -28,13 +28,13 @@
 			return p;
 		}
 
-        public static Promise<T> InvokeNewThread<T>(Func<T> func) {
+        public static IPromise<T> InvokeNewThread<T>(Func<T> func) {
             var p = new Promise<T>();
 
             var caller = TraceContext.Snapshot();
 
             var worker = new Thread(() => {
-                TraceContext.Transfer(caller);
+                TraceContext.Fork(caller);
                 try {
                     p.Resolve(func());
                 } catch (Exception e) {
@@ -46,5 +46,26 @@
 
             return p;
         }
+
+
+        public static IPromiseBase InvokeNewThread(Action func) {
+            var p = new Promise<object>();
+
+            var caller = TraceContext.Snapshot();
+
+            var worker = new Thread(() => {
+                TraceContext.Fork(caller);
+                try {
+                    func();
+                    p.Resolve();
+                } catch (Exception e) {
+                    p.Reject(e);
+                }
+            });
+            worker.IsBackground = true;
+            worker.Start();
+
+            return p;
+        }
 	}
 }