changeset 205:8200ab154c8a v2

Added ResetState to RunnableComponent to reset in case of failure Added StateChanged event to IRunnable Renamed Promise.SUCCESS -> Promise.Success Added Promise.FromException Renamed Bundle -> PromiseAll in PromiseExtensions
author cin
date Tue, 25 Oct 2016 17:40:33 +0300
parents 4d9830a9bbb8
children 86b61d53b7db
files Implab.Test/AsyncTests.cs Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj Implab.Test/Implab.Test.mono.csproj Implab.Test/Mock/MockPollingComponent.cs Implab.Test/Mock/MockRunnableComponent.cs Implab.Test/PollingComponentTests.cs Implab.Test/RunnableComponentTests.cs Implab.Test/packages.config Implab/Automaton/AutomatonTransition.cs Implab/Components/IInitializable.cs Implab/Components/IRunnable.cs Implab/Components/PollingComponent.cs Implab/Components/RunnableComponent.cs Implab/Components/StateChangeEventArgs.cs Implab/Diagnostics/TextFileListener.cs Implab/Implab.csproj Implab/Promise.cs Implab/PromiseExtensions.cs Implab/Safe.cs
diffstat 19 files changed, 447 insertions(+), 239 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Test/AsyncTests.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab.Test/AsyncTests.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -336,7 +336,7 @@
                     Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1);
                 }
             )
-                .Bundle()
+                .PromiseAll()
                 .Join();
 
             Assert.AreEqual(count * 3, res1 + res2);
@@ -414,7 +414,7 @@
                     Console.WriteLine("done reader #2: {0} ms", Environment.TickCount - t1);
                 }
             )
-                .Bundle()
+                .PromiseAll()
                 .Join();
 
             Assert.AreEqual(summ , r1 + r2);
@@ -490,7 +490,7 @@
                     Console.WriteLine("done reader #2: {0} ms, avg chunk size: {1}", Environment.TickCount - t1, avgchunk);
                 }
             )
-                .Bundle()
+                .PromiseAll()
                 .Join();
 
             Assert.AreEqual(summ , r1 + r2);
@@ -593,7 +593,7 @@
                     Console.WriteLine("done reader #2: {0} ms, {1} items", Environment.TickCount - t1, count);
                 }
             )
-                .Bundle()
+                .PromiseAll()
                 .Join();
 
             Assert.AreEqual(summ , r1 + r2);
@@ -835,7 +835,7 @@
                             log.Enqueue("Writer #1 lock released");
                         }
                     }
-                ).Bundle().Join(1000);
+                ).PromiseAll().Join(1000);
                 log.Enqueue("Done");
             } catch(Exception error) {
                 log.Enqueue(error.Message);
--- a/Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj	Tue Oct 25 17:40:33 2016 +0300
@@ -1,52 +1,55 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>8.0.30703</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{4D364996-7ECD-4193-8F90-F223FFEA49DA}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <RootNamespace>Implab.Format.Test</RootNamespace>
-    <AssemblyName>Implab.Format.Test</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
-    <ReleaseVersion>0.2</ReleaseVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>full</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="nunit.framework">
-      <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
-    </Reference>
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="JsonTests.cs" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <ProjectReference Include="..\..\Implab\Implab.csproj">
-      <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
-      <Name>Implab</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="packages.config" />
-  </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{4D364996-7ECD-4193-8F90-F223FFEA49DA}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>Implab.Format.Test</RootNamespace>
+    <AssemblyName>Implab.Format.Test</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <ReleaseVersion>0.2</ReleaseVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>full</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="nunit.framework">
+      <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="JsonTests.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ItemGroup>
+    <ProjectReference Include="..\..\Implab\Implab.csproj">
+      <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
+      <Name>Implab</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
 </Project>
\ No newline at end of file
--- a/Implab.Test/Implab.Test.mono.csproj	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab.Test/Implab.Test.mono.csproj	Tue Oct 25 17:40:33 2016 +0300
@@ -1,75 +1,81 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>8.0.30703</ProductVersion>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>{2BD05F84-E067-4B87-9477-FDC2676A21C6}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <RootNamespace>Implab.Test</RootNamespace>
-    <AssemblyName>Implab.Test</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
-    <ReleaseVersion>0.2</ReleaseVersion>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;MONO</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-    <DefineConstants>MONO</DefineConstants>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug</OutputPath>
-    <DefineConstants>DEBUG;TRACE;NET_4_5;MONO</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release</OutputPath>
-    <DefineConstants>NET_4_5;MONO</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <ConsolePause>false</ConsolePause>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="nunit.framework" />
-  </ItemGroup>
-  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup>
-    <Compile Include="AsyncTests.cs" />
-    <Compile Include="PromiseHelper.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="CancelationTests.cs" />
-    <Compile Include="RunnableComponentTests.cs" />
-    <Compile Include="PollingComponentTests.cs" />
-    <Compile Include="Mock\MockRunnableComponent.cs" />
-    <Compile Include="Mock\MockPollingComponent.cs" />
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\Implab\Implab.csproj">
-      <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
-      <Name>Implab</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Mock\" />
-  </ItemGroup>
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{2BD05F84-E067-4B87-9477-FDC2676A21C6}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>Implab.Test</RootNamespace>
+    <AssemblyName>Implab.Test</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <ReleaseVersion>0.2</ReleaseVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;MONO</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+    <DefineConstants>MONO</DefineConstants>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug 4.5|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;TRACE;NET_4_5;MONO</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release 4.5|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <DefineConstants>NET_4_5;MONO</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
+      <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <ItemGroup>
+    <Compile Include="AsyncTests.cs" />
+    <Compile Include="PromiseHelper.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="CancelationTests.cs" />
+    <Compile Include="RunnableComponentTests.cs" />
+    <Compile Include="PollingComponentTests.cs" />
+    <Compile Include="Mock\MockRunnableComponent.cs" />
+    <Compile Include="Mock\MockPollingComponent.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Implab\Implab.csproj">
+      <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project>
+      <Name>Implab</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
 </Project>
\ No newline at end of file
--- a/Implab.Test/Mock/MockPollingComponent.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab.Test/Mock/MockPollingComponent.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -50,7 +50,7 @@
         }
 
         protected override IPromise OnTick(ICancellationToken cancellationToken) {
-            return MockTick != null ? Safe.Run(() => MockTick(cancellationToken)) : Promise.SUCCESS;
+            return MockTick != null ? Safe.Run(() => MockTick(cancellationToken)) : Promise.Success;
         }
 
         protected override void OnTickCancel(Exception error) {
--- a/Implab.Test/Mock/MockRunnableComponent.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab.Test/Mock/MockRunnableComponent.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -6,6 +6,9 @@
         public MockRunnableComponent(bool initialized) : base(initialized) {
         }
 
+        public MockRunnableComponent(bool initialized, bool reusable) : base(initialized, reusable) {
+        }
+
         public Action MockInit {
             get;
             set;
@@ -21,6 +24,11 @@
             set;
         }
 
+        public Action<bool, Exception> MockDispose {
+            get;
+            set;
+        }
+
         protected override IPromise OnStart() {
             return MockStart != null ? Safe.Run(MockStart).Chain(base.OnStart) : Safe.Run(base.OnStart);
         }
@@ -33,6 +41,12 @@
             if (MockInit != null)
                 MockInit();
         }
+
+        protected override void Dispose(bool disposing, Exception lastError) {
+            if (MockDispose != null)
+                MockDispose(disposing, lastError);
+            base.Dispose(disposing, lastError);
+        }
     }
 }
 
--- a/Implab.Test/PollingComponentTests.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab.Test/PollingComponentTests.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -36,7 +36,7 @@
 
             Assert.AreEqual(ExecutionState.Created, comp.State);
 
-            comp.Init();
+            comp.Initialize();
 
             Assert.AreEqual(ExecutionState.Ready, comp.State);
 
@@ -57,7 +57,7 @@
             var comp = new MockPollingComponent(TimeSpan.FromMilliseconds(1), null, true);
             comp.MockTick = ct => {
                 signal.Set();
-                return Promise.SUCCESS;
+                return Promise.Success;
             };
 
             comp.Start().Join(1000);
--- a/Implab.Test/RunnableComponentTests.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab.Test/RunnableComponentTests.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -39,7 +39,7 @@
 
             Assert.AreEqual(ExecutionState.Created, comp.State);
 
-            comp.Init();
+            comp.Initialize();
 
             Assert.AreEqual(ExecutionState.Ready, comp.State);
 
@@ -65,7 +65,7 @@
             ShouldThrow(() => comp.Stop());
             Assert.AreEqual(ExecutionState.Created, comp.State);
 
-            ShouldThrow(comp.Init);
+            ShouldThrow(comp.Initialize);
 
             Assert.AreEqual(ExecutionState.Failed, comp.State);
 
@@ -85,12 +85,77 @@
 
             ShouldThrow(() => comp.Start());
             ShouldThrow(() => comp.Stop());
-            ShouldThrow(comp.Init);
+            ShouldThrow(comp.Initialize);
 
             Assert.AreEqual(ExecutionState.Disposed, comp.State);
         }
 
         [TestMethod]
+        public void ShouldCallDisposeOnStop() {
+            var comp = new MockRunnableComponent(true);
+
+            bool disposed = false;
+            comp.MockDispose = (disposing, error) => {
+                disposed = true;
+            };
+
+            comp.Start().Join(1000);
+            comp.Stop().Join(1000);
+
+            ShouldThrow(() => comp.Start());
+            ShouldThrow(() => comp.Stop());
+            ShouldThrow(comp.Initialize);
+
+            Assert.AreEqual(ExecutionState.Disposed, comp.State);
+            Assert.IsTrue(disposed);
+        }
+
+        [TestMethod]
+        public void ShouldNotCallDisposeOnStop() {
+            var comp = new MockRunnableComponent(true, true);
+
+            bool disposed = false;
+            comp.MockDispose = (disposing, error) => {
+                disposed = true;
+            };
+
+            comp.Start().Join(1000);
+            comp.Stop().Join(1000);
+
+            Assert.AreEqual(ExecutionState.Ready, comp.State);
+            Assert.IsFalse(disposed);
+        }
+
+        [TestMethod]
+        public void SelfDisposeOnStop() {
+            var comp = new MockRunnableComponent(true, true);
+
+            bool disposed = false;
+            Exception lastError = null;
+            comp.MockDispose = (disposing, error) => {
+                disposed = true;
+                lastError = error;
+            };
+
+            comp.Start().Join(1000);
+            comp.Stop().Join(1000);
+
+            Assert.AreEqual(ExecutionState.Ready, comp.State);
+            Assert.IsFalse(disposed);
+
+            comp.MockStop = () => {
+                comp.Dispose();
+                return Promise.Success;
+            };
+
+            comp.Start().Join(1000);
+            comp.Stop().Join(1000);
+
+            Assert.AreEqual(ExecutionState.Disposed, comp.State);
+            Assert.IsTrue(disposed);
+        }
+
+        [TestMethod]
         public void StartCancelTest() {
             var comp = new MockRunnableComponent(true) {
                 MockStart = () => PromiseHelper.Sleep(100000, 0)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab.Test/packages.config	Tue Oct 25 17:40:33 2016 +0300
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="NUnit" version="2.6.4" targetFramework="net45" />
+</packages>
\ No newline at end of file
--- a/Implab/Automaton/AutomatonTransition.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Automaton/AutomatonTransition.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -28,6 +28,14 @@
         public override int GetHashCode() {
             return s1 + s2 + edge;
         }
+
+        public static bool operator == (AutomatonTransition rv, AutomatonTransition lv) {
+            return rv.Equals(lv);
+        }
+
+        public static bool operator !=(AutomatonTransition rv, AutomatonTransition lv) {
+            return rv.Equals(lv);
+        }
     }
 }
 
--- a/Implab/Components/IInitializable.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Components/IInitializable.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -3,8 +3,8 @@
 namespace Implab.Components {
     /// <summary>
     /// Initializable components are created and initialized in two steps, first we have create the component,
-    /// then we have to complete it's creation by calling an <see cref="Init()"/> method. All parameters needed
-    /// to complete the initialization must be passed before the calling <see cref="Init()"/>
+    /// then we have to complete it's creation by calling an <see cref="Initialize()"/> method. All parameters needed
+    /// to complete the initialization must be passed before the calling <see cref="Initialize()"/>
     /// </summary>
     public interface IInitializable {
         /// <summary>
@@ -12,10 +12,10 @@
         /// </summary>
         /// <remarks>
         /// Normally virtual methods shouldn't be called from the constructor, due to the incomplete object state, but
-        /// they can be called from this method. This method is also usefull when we constructing a complex grpah
+        /// they can be called from this method. This method is aьуерщlso usefull when we constructing a complex grpah
         /// of components where cyclic references may take place.
         /// </remarks>
-        void Init();
+        void Initialize();
     }
 }
 
--- a/Implab/Components/IRunnable.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Components/IRunnable.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -14,6 +14,8 @@
 
         ExecutionState State { get; }
 
+        event EventHandler<StateChangeEventArgs> StateChanged;
+
         Exception LastError { get; }
     }
 }
--- a/Implab/Components/PollingComponent.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Components/PollingComponent.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -124,7 +124,7 @@
         /// Invoked when the timer ticks, use this method to implement your logic
         /// </summary>
         protected virtual IPromise OnTick(ICancellationToken cancellationToken) {
-            return Promise.SUCCESS;
+            return Promise.Success;
         }
 
         protected override IPromise OnStop() {
--- a/Implab/Components/RunnableComponent.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Components/RunnableComponent.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -9,7 +9,8 @@
             Start,
             Stop,
             Dispose,
-            Last = Dispose
+            Reset,
+            Last = Reset
         }
 
         class StateMachine {
@@ -37,9 +38,11 @@
                 Edge(ExecutionState.Running, ExecutionState.Disposed, Commands.Dispose);
 
                 Edge(ExecutionState.Stopping, ExecutionState.Failed, Commands.Fail);
-                Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Ok);
+                Edge(ExecutionState.Stopping, ExecutionState.Ready, Commands.Ok);
+                Edge(ExecutionState.Stopping, ExecutionState.Disposed, Commands.Dispose);
 
                 Edge(ExecutionState.Failed, ExecutionState.Disposed, Commands.Dispose);
+                Edge(ExecutionState.Failed, ExecutionState.Initializing, Commands.Reset);
             }
 
             static void Edge(ExecutionState s1, ExecutionState s2, Commands cmd) {
@@ -67,11 +70,26 @@
         IPromise m_pending;
         Exception m_lastError;
 
-        readonly StateMachine m_stateMachine;
-
-        protected RunnableComponent(bool initialized) {
+        readonly StateMachine m_stateMachine;
+        readonly bool m_reusable;
+        public event EventHandler<StateChangeEventArgs> StateChanged;
+
+        /// <summary>
+        /// Initializes component state. 
+        /// </summary>
+        /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
+        /// <param name="reusable">If set, the component may start after it has been stopped, otherwise the component is disposed after being stopped.</param>
+        protected RunnableComponent(bool initialized, bool reusable) {
             m_stateMachine = new StateMachine(initialized ? ExecutionState.Ready : ExecutionState.Created);
-            DisposeTimeout = 10000;
+            m_reusable = reusable;
+            DisposeTimeout = 10000;
+        }
+
+        /// <summary>
+        /// Initializes component state. The component created with this constructor is not reusable, i.e. it will be disposed after stop.
+        /// </summary>
+        /// <param name="initialized">If set, the component initial state is <see cref="ExecutionState.Ready"/> and the component is ready to start, otherwise initialization is required.</param>
+        protected RunnableComponent(bool initialized) : this(initialized, false) {
         }
 
         /// <summary>
@@ -84,49 +102,119 @@
 
         void ThrowInvalidCommand(Commands cmd) {
             if (m_stateMachine.State == ExecutionState.Disposed)
-                throw new ObjectDisposedException(ToString());
-            
-            throw new InvalidOperationException(String.Format("Commnd {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
+                throw new ObjectDisposedException(ToString());
+
+            throw new InvalidOperationException(String.Format("Command {0} is not allowed in the state {1}", cmd, m_stateMachine.State));
+        }
+
+        bool MoveIfInState(Commands cmd, IPromise pending, Exception error, ExecutionState state) {
+            ExecutionState prev, current;
+            lock (m_stateMachine) {
+                if (m_stateMachine.State != state)
+                    return false;
+
+                prev = m_stateMachine.State;
+                if (!m_stateMachine.Move(cmd))
+                    ThrowInvalidCommand(cmd);
+                current = m_stateMachine.State;
+
+                m_pending = pending;
+                m_lastError = error;
+            }
+            if (prev != current)
+                OnStateChanged(prev, current, error);
+            return true;
         }
 
-        void Move(Commands cmd) {
-            if (!m_stateMachine.Move(cmd))
-                ThrowInvalidCommand(cmd);
+        bool MoveIfPending(Commands cmd, IPromise pending, Exception error, IPromise expected) {
+            ExecutionState prev, current;
+            lock (m_stateMachine) {
+                if (m_pending != expected)
+                    return false;
+                prev = m_stateMachine.State;
+                if (!m_stateMachine.Move(cmd))
+                    ThrowInvalidCommand(cmd);
+                current = m_stateMachine.State;
+                m_pending = pending;
+                m_lastError = error;
+            }
+            if (prev != current)
+                OnStateChanged(prev, current, error);
+            return true;
+        }
+
+        IPromise Move(Commands cmd, IPromise pending, Exception error) {
+            ExecutionState prev, current;
+            IPromise ret;
+            lock (m_stateMachine) {
+                prev = m_stateMachine.State;
+                if (!m_stateMachine.Move(cmd))
+                    ThrowInvalidCommand(cmd);
+                current = m_stateMachine.State;
+
+                ret = m_pending;
+                m_pending = pending;
+                m_lastError = error;
+                
+            }
+            if(prev != current)
+                OnStateChanged(prev, current, error);
+            return ret;
+        }
+
+        protected virtual void OnStateChanged(ExecutionState previous, ExecutionState current, Exception error) {
+            var h = StateChanged;
+            if (h != null)
+                h(this, new StateChangeEventArgs {
+                    State = current,
+                    LastError = error
+                });
         }
 
         /// <summary>
         /// Moves the component from running to failed state.
         /// </summary>
         /// <param name="error">The exception which is describing the error.</param>
-        /// <returns>Returns true if the component is set to the failed state, false - otherwise.
-        /// This method works only for the running state, in any other state it will return false.</returns>
-        protected bool Fail(Exception error) {
-            lock (m_stateMachine) {
-                if(m_stateMachine.State == ExecutionState.Running) {
-                    m_stateMachine.Move(Commands.Fail);
-                    m_lastError = error;
-                    return true;
-                }
-            }
-            return false;
+        protected bool Fail(Exception error) {
+            return MoveIfInState(Commands.Fail, null, error, ExecutionState.Running);
         }
 
-        void Invoke(Commands cmd, Action action) {
-            lock (m_stateMachine) 
-                Move(cmd);
-            
+        /// <summary>
+        /// Tries to reset <see cref="ExecutionState.Failed"/> state to <see cref="ExecutionState.Ready"/>.
+        /// </summary>
+        /// <returns>True if component is reset to <see cref="ExecutionState.Ready"/>, false if the componet wasn't
+        /// in <see cref="ExecutionState.Failed"/> state.</returns>
+        /// <remarks>
+        /// This method checks the current state of the component and if it's in <see cref="ExecutionState.Failed"/>
+        /// moves component to <see cref="ExecutionState.Initializing"/>.
+        /// The <see cref="OnResetState()"/> is called and if this method completes succesfully the component moved
+        /// to <see cref="ExecutionState.Ready"/> state, otherwise the component is moved to <see cref="ExecutionState.Failed"/>
+        /// state. If <see cref="OnResetState()"/> throws an exception it will be propagated by this method to the caller.
+        /// </remarks>
+        protected bool ResetState() {
+            if (!MoveIfInState(Commands.Reset, null, null, ExecutionState.Failed))
+                return false;
+
             try {
-                action();
-                lock(m_stateMachine)
-                    Move(Commands.Ok);
-
+                OnResetState();
+                Move(Commands.Ok, null, null);
+                return true;
             } catch (Exception err) {
-                lock (m_stateMachine) {
-                    Move(Commands.Fail);
-                    m_lastError = err;
-                }
+                Move(Commands.Fail, null, err);
                 throw;
-            }
+            }
+        }
+
+        /// <summary>
+        /// This method is called by <see cref="ResetState"/> to reinitialize component in the failed state.
+        /// </summary>
+        /// <remarks>
+        /// Default implementation throws <see cref="NotImplementedException"/> which will cause the component
+        /// fail to reset it's state and it left in <see cref="ExecutionState.Failed"/> state.
+        /// If this method doesn't throw exceptions the component is moved to <see cref="ExecutionState.Ready"/> state.
+        /// </remarks>
+        protected virtual void OnResetState() {
+            throw new NotImplementedException();
         }
 
         IPromise InvokeAsync(Commands cmd, Func<IPromise> action, Action<IPromise, IDeferred> chain) {
@@ -135,40 +223,20 @@
 
             var task = new ActionChainTask(action, null, null, true);
 
-            lock (m_stateMachine) {
-                Move(cmd);
-            
-                prev = m_pending;
+            Action<Exception> errorOrCancel = e => {
+                if (e == null)
+                    e = new OperationCanceledException();
+                MoveIfPending(Commands.Fail, null, e, promise);
+                throw new PromiseTransientException(e);
+            };
 
-                Action<Exception> errorOrCancel = e => {
-                    if (e == null)
-                        e = new OperationCanceledException();
-                    
-                    lock (m_stateMachine) {
-                        if (m_pending == promise) {
-                            Move(Commands.Fail);
-                            m_pending = null;
-                            m_lastError = e;
-                        }
-                    }
-                    throw new PromiseTransientException(e);
-                };
+            promise = task.Then(
+                () => MoveIfPending(Commands.Ok, null, null, promise),
+                errorOrCancel,
+                errorOrCancel
+            );
 
-                promise = task.Then(
-                    () => {
-                        lock(m_stateMachine) {
-                            if (m_pending == promise) {
-                                Move(Commands.Ok);
-                                m_pending = null;
-                            }
-                        }
-                    },
-                    errorOrCancel,
-                    errorOrCancel 
-                );
-
-                m_pending = promise;
-            }
+            prev = Move(cmd, promise, null);
 
             if (prev == null)
                 task.Resolve();
@@ -181,8 +249,16 @@
 
         #region IInitializable implementation
 
-        public void Init() {
-            Invoke(Commands.Init, OnInitialize);
+        public void Initialize() {
+            Move(Commands.Init, null, null);
+
+            try {
+                OnInitialize();
+                Move(Commands.Ok, null, null);
+            } catch (Exception err) {
+                Move(Commands.Fail, null, err);
+                throw;
+            }
         }
 
         protected virtual void OnInitialize() {
@@ -197,15 +273,16 @@
         }
 
         protected virtual IPromise OnStart() {
-            return Promise.SUCCESS;
+            return Promise.Success;
         }
 
         public IPromise Stop() {
-            return InvokeAsync(Commands.Stop, OnStop, StopPending).Then(Dispose);
+            var pending = InvokeAsync(Commands.Stop, OnStop, StopPending);
+            return m_reusable ? pending : pending.Then(Dispose);
         }
 
         protected virtual IPromise OnStop() {
-            return Promise.SUCCESS;
+            return Promise.Success;
         }
 
         /// <summary>
@@ -258,17 +335,14 @@
         /// </para></remarks>
         public void Dispose() {
             IPromise pending;
+
             lock (m_stateMachine) {
                 if (m_stateMachine.State == ExecutionState.Disposed)
                     return;
-
-                Move(Commands.Dispose);
+                pending = Move(Commands.Dispose, null, null);
+            }
 
-                GC.SuppressFinalize(this);
-
-                pending = m_pending;
-                m_pending = null;
-            }
+            GC.SuppressFinalize(this);
             if (pending != null) {
                 pending.Cancel();
                 pending.Timeout(DisposeTimeout).On(
@@ -277,7 +351,7 @@
                     reason => Dispose(true, new OperationCanceledException("The operation is cancelled", reason))
                 );
             } else {
-                Dispose(true, m_lastError);
+                Dispose(true, null);
             }
         }
 
@@ -287,6 +361,11 @@
 
         #endregion
 
+        /// <summary>
+        /// Releases all resources used by the component, called automatically, override this method to implement your cleanup.
+        /// </summary>
+        /// <param name="disposing">true if this method is called during normal dispose process.</param>
+        /// <param name="lastError">The last error which occured during the component stop.</param>
         protected virtual void Dispose(bool disposing, Exception lastError) {
 
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Components/StateChangeEventArgs.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -0,0 +1,16 @@
+using System;
+
+namespace Implab.Components
+{
+    public class StateChangeEventArgs {
+        /// <summary>
+        /// The error information if any
+        /// </summary>
+        public Exception LastError { get; set; }
+
+        /// <summary>
+        /// The state of the service corresponding to this event
+        /// </summary>
+        public ExecutionState State { get; set; }
+    }
+}
--- a/Implab/Diagnostics/TextFileListener.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Diagnostics/TextFileListener.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -5,6 +5,7 @@
 namespace Implab.Diagnostics {
     public class TextFileListener: ListenerBase {
         readonly TextWriter m_textWriter;
+        readonly object m_lock = new object();
 
         public TextFileListener(string fileName) {
             m_textWriter = File.CreateText(fileName);
@@ -20,7 +21,7 @@
                 msg.Append("  ");
             msg.AppendFormat("[{0}]:{1}: {2}", args.ThreadId, args.Channel, entry);
 
-            lock (m_textWriter) {
+            lock (m_lock) {
                 if (!IsDisposed) {
                     // тут гарантировано еще не освобожден m_textWriter
                     m_textWriter.WriteLine(msg);
@@ -35,7 +36,7 @@
             base.Dispose(disposing);
             if (disposing) {
                 // IsDisposed = true
-                lock (m_textWriter) {
+                lock (m_lock) {
                     Safe.Dispose(m_textWriter);
                 }
             }
--- a/Implab/Implab.csproj	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Implab.csproj	Tue Oct 25 17:40:33 2016 +0300
@@ -75,6 +75,7 @@
     <Reference Include="mscorlib" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Components\StateChangeEventArgs.cs" />
     <Compile Include="CustomEqualityComparer.cs" />
     <Compile Include="Diagnostics\ConsoleTraceListener.cs" />
     <Compile Include="Diagnostics\LogChannel.cs" />
@@ -269,10 +270,5 @@
       </Properties>
     </MonoDevelop>
   </ProjectExtensions>
-  <ItemGroup>
-    <Folder Include="Components\" />
-    <Folder Include="Automaton\RegularExpressions\" />
-    <Folder Include="Formats\" />
-    <Folder Include="Formats\JSON\" />
-  </ItemGroup>
+  <ItemGroup />
 </Project>
\ No newline at end of file
--- a/Implab/Promise.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Promise.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -3,11 +3,10 @@
 
 namespace Implab {
     public class Promise : AbstractPromise, IDeferred {
-        public static readonly Promise SUCCESS;
+        public static readonly IPromise Success;
 
         static Promise() {
-            SUCCESS = new Promise();
-            SUCCESS.Resolve();
+            Success = new SuccessPromise();
         }
 
         public void Resolve() {
@@ -16,7 +15,11 @@
 
         public void Reject(Exception error) {
             SetError(error);
-        }
+        }
+
+        public static IPromise FromException(Exception exception) {
+            return new FailedPromise(exception);
+        }
     }
 }
 
--- a/Implab/PromiseExtensions.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/PromiseExtensions.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -2,7 +2,8 @@
 using System;
 using Implab.Diagnostics;
 using System.Collections.Generic;
-
+using System.Linq;
+
 namespace Implab {
     public static class PromiseExtensions {
         public static IPromise<T> DispatchToCurrentContext<T>(this IPromise<T> that) {
@@ -75,7 +76,7 @@
             ((ICancellable)cookie).Cancel(new TimeoutException());
         }
 
-        /// <summary>
+/// <summary>
         /// Cancells promise after the specified timeout is elapsed.
         /// </summary>
         /// <param name="that">The promise to cancel on timeout.</param>
@@ -88,7 +89,17 @@
             return that;
         }
 
-        public static IPromise Bundle(this ICollection<IPromise> that) {
+        public static IPromise PromiseAll(this IEnumerable<IPromise> that) {
+            Safe.ArgumentNotNull(that, "that");
+            return PromiseAll(that.ToList());
+        }
+
+        public static IPromise<T[]> PromiseAll<T>(this IEnumerable<IPromise<T>> that) {
+            Safe.ArgumentNotNull(that, "that");
+            return PromiseAll(that.ToList());
+        }
+
+        public static IPromise PromiseAll(this ICollection<IPromise> that) {
             Safe.ArgumentNotNull(that, "that");
 
             int count = that.Count;
@@ -128,7 +139,7 @@
             return medium;
         }
 
-        public static IPromise<T[]> Bundle<T>(this ICollection<IPromise<T>> that) {
+        public static IPromise<T[]> PromiseAll<T>(this ICollection<IPromise<T>> that) {
             Safe.ArgumentNotNull(that, "that");
 
             int count = that.Count;
--- a/Implab/Safe.cs	Tue Oct 18 17:49:54 2016 +0300
+++ b/Implab/Safe.cs	Tue Oct 25 17:40:33 2016 +0300
@@ -83,7 +83,7 @@
 
             try {
                 action();
-                return Promise.SUCCESS;
+                return Promise.Success;
             } catch (Exception err) {
                 return new FailedPromise(err);
             }