changeset 256:c52691faaf21 v3

Removed obsolete App, ComponentContainer Extracted IAsyncComponent interface Working on RunnableComponent
author cin
date Wed, 11 Apr 2018 03:05:14 +0300
parents b00441e04738
children 440801d88019
files Implab.Test/Implab.Test.csproj Implab.Test/UnitTest1.cs Implab/Components/App.cs Implab/Components/ComponentContainer.cs Implab/Components/DisposablePool.cs Implab/Components/IAsyncComponent.cs Implab/Components/IRunnable.cs Implab/Components/RunnableComponent.cs
diffstat 8 files changed, 85 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/Implab.Test/Implab.Test.csproj	Wed Apr 04 15:38:48 2018 +0300
+++ b/Implab.Test/Implab.Test.csproj	Wed Apr 11 03:05:14 2018 +0300
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <TargetFramework>net46</TargetFramework>
-    <FrameworkPathOverride Condition="'$(TargetFramework)'=='net45' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride>
 
     <IsPackable>false</IsPackable>
   </PropertyGroup>
--- a/Implab.Test/UnitTest1.cs	Wed Apr 04 15:38:48 2018 +0300
+++ b/Implab.Test/UnitTest1.cs	Wed Apr 11 03:05:14 2018 +0300
@@ -2,6 +2,7 @@
 using System.Diagnostics;
 using System.Threading;
 using Implab.Diagnostics;
+using System.Linq;
 using Xunit;
 
 namespace Implab.Test {
@@ -11,19 +12,29 @@
         [Fact]
         public async Task Test1() {
             var listener = new SimpleTraceListener(Console.Out);
+            listener.TraceOutputOptions |= TraceOptions.ThreadId;
 
             var source = TraceSource;
             source.Switch.Level = SourceLevels.All;
 
             source.Listeners.Add(listener);
 
-            using (var op = LogicalOperation(nameof(Test1)))
-            using (LogicalOperation("InnerOperation")){
+            using (LogicalOperation("Test1")){
                 await Task.Yield();
-                Log("Inner");
-                await Task.Yield();
-                Log("source event");
+                Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString())));
+                await AsyncDummy();
+                Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString())));
             }
         }
+
+        async Task AsyncDummy() {
+            using(LogicalOperation("OuterDummy"))
+            using(LogicalOperation("InnerDummy")) {
+                Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString())));
+                await Task.Delay(1);
+                Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString())));
+            }
+            Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast<object>().Select(x => x.ToString())));
+        }
     }
 }
--- a/Implab/Components/App.cs	Wed Apr 04 15:38:48 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Implab.Components {
-    /// <summary>
-    /// Global application components and services.
-    /// </summary>
-    public static class App {
-        readonly static ComponentContainer<object> _root = new ComponentContainer<object>();
-
-        /// <summary>
-        /// The container for application level components.
-        /// </summary>
-        /// <remarks>Pools of disposable objects can be placed here and they will be automatically
-        /// disposed when application domain is unloaded.</remarks>
-        public static ICollection<object> RootContainer {
-            get { return _root; }
-        }
-
-        static App() {
-            AppDomain.CurrentDomain.DomainUnload += (sender, e) => _root.Dispose();
-        }
-    }
-}
-
--- a/Implab/Components/ComponentContainer.cs	Wed Apr 04 15:38:48 2018 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Implab.Components {
-    /// <summary>
-    /// Component container, used to store track components in multi-threaded environmment.
-    /// </summary>
-    /// <remarks>Instanses of this class are thread safe.</remarks>
-    public class ComponentContainer<T> : Disposable, ICollection<T> {
-        List<T> m_components = new List<T>();
-        readonly object m_lock = new object();
-
-        /// <summary>
-        /// Removes currently stored compoenents from the container and disposes them if possible.
-        /// </summary>
-        /// <remarks>
-        /// A new components may be added before this method completes.
-        /// </remarks>
-        public void Clear() {
-            List<T> removed;
-
-            lock (m_lock) {
-                removed = m_components;
-                m_components = new List<T>();
-            }
-
-            foreach (var item in removed.OfType<IDisposable>())
-                item.Dispose();
-        }
-
-        /// <summary>
-        /// Checks whether the specified item in the collection.
-        /// </summary>
-        /// <param name="item">The item to check.</param>
-        public bool Contains(T item) {
-            lock (m_lock)
-                return m_components.Contains(item);
-        }
-
-        /// <summary>
-        /// Copies currently stored components to the specified array.
-        /// </summary>
-        /// <param name="array">A destination array for components.</param>
-        /// <param name="arrayIndex">A starting index in the destination array.</param>
-        public void CopyTo(T[] array, int arrayIndex) {
-            lock (m_lock)
-                m_components.CopyTo(array, arrayIndex);
-        }
-
-        /// <summary>
-        /// Remove the specified item from the collection.
-        /// </summary>
-        /// <param name="item">The item to remove.</param>
-        public bool Remove(T item) {
-            lock (m_lock)
-                return m_components.Remove(item);
-        }
-
-        /// <summary>
-        /// Gets the count of components in the collection.
-        /// </summary>
-        public int Count {
-            get {
-                lock (m_lock)
-                    return m_components.Count;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether this instance is read only.
-        /// </summary>
-        /// <remarks>
-        /// Always false.
-        /// </remarks>
-        public bool IsReadOnly {
-            get {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Gets the enumerator for components in the collection.
-        /// </summary>
-        /// <returns>The enumerator.</returns>
-        public IEnumerator<T> GetEnumerator() {
-            T[] items = new T[m_components.Count];
-            lock (m_lock) {
-                m_components.CopyTo(items);
-            }
-            return (IEnumerator<T>)items.GetEnumerator();
-        }
-
-        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
-            return GetEnumerator();
-        }
-
-        /// <summary>
-        /// Add the specified item to the collection.
-        /// </summary>
-        /// <param name="item">The item to add.</param>
-        /// <remarks>
-        /// If the collection is alredy disposed, the item isn't added to the collection and disposed if possible.
-        /// </remarks>
-        public void Add(T item) {
-            Safe.ArgumentNotNull(item, "item");
-            bool dispose = false;
-            lock (m_lock) {
-                if (IsDisposed)
-                    dispose = true;
-                else
-                    m_components.Add(item);
-            }
-            if (dispose)
-                Safe.Dispose(item);
-        }
-
-        /// <summary>
-        /// Disposes the components stored in the collection.
-        /// </summary>
-        /// <param name="disposing">If set to <c>true</c> the collection is disposing.</param>
-        protected override void Dispose(bool disposing) {
-            if (disposing)
-                Clear();
-
-            base.Dispose(disposing);
-        }
-    }
-}
-
--- a/Implab/Components/DisposablePool.cs	Wed Apr 04 15:38:48 2018 +0300
+++ b/Implab/Components/DisposablePool.cs	Wed Apr 11 03:05:14 2018 +0300
@@ -6,10 +6,10 @@
 
 namespace Implab.Components {
     /// <summary>
-    /// The base class for implementing pools of disposable objects.
+    /// The base class for implementing thread-safe pools of disposable objects.
     /// </summary>
     /// <remarks>
-    /// <para>This class maintains a set of pre-created objects and which are frequently allocated and released
+    /// <para>This class maintains a set of pre-created objects which are frequently allocated and released
     /// by clients. The pool maintains maximum number of unsued object, any object above this limit is disposed,
     /// if the pool is empty it will create new objects on demand.</para>
     /// <para>Instances of this class are thread-safe.</para>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Implab/Components/IAsyncComponent.cs	Wed Apr 11 03:05:14 2018 +0300
@@ -0,0 +1,37 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Implab.Components {
+    /// <summary>
+    /// An interface for asynchronous components.
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// Асинхронные компоненты не предназначены для одновременного использования несколькими клиентами,
+    /// однако существуют внутренние процессы, изменяющее состояние компонент без участия клиента.
+    /// Данный интерфейс определяет протокол взаимодействия с компонентой, при которм компоненте
+    /// посылаются сигналы от клиента, в ответ на которые компонента меняет свойство <see cref="Completion"/>,
+    /// данное свойство содержит в себе новую задачу, выполняемую компонентой и данное свойство
+    /// может измениться только при получении нового сигнала от клиента.
+    /// </para>
+    /// <para>
+    /// В дополнение к <see cref="Completion"/> компонента может определять другие свойства, в
+    /// которых будет передаваться информация о результате выполнения операции.
+    /// </para>
+    /// <para>
+    /// Особое внимание следует уделить реализации <see cref="IDisposable"/>, который по своей природе
+    /// синхронный, данное правило безусловно можно нарушить, но тогда могут возникнуть проблемы с
+    /// тем, что ресурсы еще не освободились, а ход программы продолжается, что приведет к ошибкам,
+    /// например при попытке получить ресуср другим объектом, либо при заврешении программы.
+    /// </para>
+    /// <seealso href="https://blog.stephencleary.com/2013/01/async-oop-0-introduction.html"/>
+    /// </remarks>
+    public interface IAsyncComponent {
+        /// <summary>
+        /// The result of the last started operation. This property reflects
+        /// only the result of the last started operation and therefore should
+        /// change only if a new operation is initiated.
+        /// </summary>
+        Task Completion { get; }
+    }
+}
\ No newline at end of file
--- a/Implab/Components/IRunnable.cs	Wed Apr 04 15:38:48 2018 +0300
+++ b/Implab/Components/IRunnable.cs	Wed Apr 11 03:05:14 2018 +0300
@@ -34,14 +34,7 @@
         void Stop(CancellationToken ct);
 
         /// <summary>
-        /// The result of the last started operation. This property reflects
-        /// only the result of the last started operation and therefore should
-        /// change only if a new operation is initiated.
-        /// </summary>
-        Task Completion { get; }
-
-        /// <summary>
-        /// Current state of the componenet
+        /// Current state of the componenet, dynamically reflects the current state.
         /// </summary>
         ExecutionState State { get; }
 
--- a/Implab/Components/RunnableComponent.cs	Wed Apr 04 15:38:48 2018 +0300
+++ b/Implab/Components/RunnableComponent.cs	Wed Apr 11 03:05:14 2018 +0300
@@ -12,7 +12,7 @@
     /// This class provides a basic lifecycle from the creation to the
     /// termination of the component.
     /// </remarks>
-    public class RunnableComponent : IRunnable, IInitializable, IDisposable {
+    public class RunnableComponent : IAsyncComponent, IRunnable, IInitializable, IDisposable {
 
         /// <summary>
         /// This class bounds <see cref="CancellationTokenSource"/> lifetime to the task,
@@ -80,7 +80,7 @@
         }
 
         // this lock is used to synchronize state flow of the component during
-        // completions or the operations.
+        // processing calls from a client and internal processes.
         readonly object m_lock = new object();
 
         // current operation cookie, used to check wheather a call to
@@ -88,6 +88,7 @@
         // operation, if cookies didn't match ignore completion result.
         object m_cookie;
 
+        // AsyncOperationDscriptor aggregates a task and it's cancellation token
         AsyncOperationDescriptor m_current = AsyncOperationDescriptor.None;
 
         ExecutionState m_state;
@@ -152,6 +153,8 @@
             var cookie = new object();
             if (MoveInitialize(cookie))
                 ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie);
+            else
+                throw new InvalidOperationException();
         }
 
         /// <summary>
@@ -171,6 +174,8 @@
             var cookie = new object();
             if (MoveStart(cookie))
                 ScheduleTask(StartInternalAsync, ct, cookie);
+            else
+                throw new InvalidOperationException();
         }
 
         protected virtual Task StartInternalAsync(CancellationToken ct) {
@@ -181,6 +186,8 @@
             var cookie = new object();
             if (MoveStop(cookie))
                 ScheduleTask(StopAsync, ct, cookie);
+            else
+                throw new InvalidOperationException();
         }
 
         async Task StopAsync(CancellationToken ct) {
@@ -196,6 +203,16 @@
             return Task.CompletedTask;
         }
 
+        protected void Fail(Exception err) {
+            lock(m_lock) {
+                if (m_state != ExecutionState.Running)
+                    return;
+                m_cookie = new object();
+                LastError = err;
+                State = ExecutionState.Failed;
+            }
+        }
+
 
         #region state management
 
@@ -256,16 +273,16 @@
             }
         }
 
-
+        void ScheduleTask(Func<CancellationToken, Task> next, CancellationToken ct, object cookie) {
 
-        protected async void ScheduleTask(Func<CancellationToken, Task> next, CancellationToken ct, object cookie) {
-            try {
-                m_current = AsyncOperationDescriptor.Create(next, ct);
-                await m_current.Task;
-                MoveSuccess(cookie);
-            } catch (Exception e) {
-                MoveFailed(e, cookie);
-            }
+            m_current = AsyncOperationDescriptor.Create(async (x) => {
+                try {
+                    await next(x);
+                    MoveSuccess(cookie);
+                } catch (Exception e) {
+                    MoveFailed(e, cookie);
+                }
+            }, ct);
         }
 
         #endregion