Mercurial > pub > ImplabNet
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