diff --git a/Implab.Test/Implab.Test.csproj b/Implab.Test/Implab.Test.csproj --- a/Implab.Test/Implab.Test.csproj +++ b/Implab.Test/Implab.Test.csproj @@ -2,7 +2,7 @@ net46 - /usr/lib/mono/4.5/ + /usr/lib/mono/4.5/ false diff --git a/Implab.Test/UnitTest1.cs b/Implab.Test/UnitTest1.cs --- a/Implab.Test/UnitTest1.cs +++ b/Implab.Test/UnitTest1.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using System.Threading; using Implab.Diagnostics; +using System.Linq; using Xunit; namespace Implab.Test { @@ -11,19 +12,29 @@ namespace Implab.Test { [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().Select(x => x.ToString()))); + await AsyncDummy(); + Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); } } + + async Task AsyncDummy() { + using(LogicalOperation("OuterDummy")) + using(LogicalOperation("InnerDummy")) { + Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); + await Task.Delay(1); + Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); + } + Log(String.Join(", ", Trace.CorrelationManager.LogicalOperationStack.Cast().Select(x => x.ToString()))); + } } } diff --git a/Implab/Components/App.cs b/Implab/Components/App.cs deleted file mode 100644 --- a/Implab/Components/App.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Implab.Components { - /// - /// Global application components and services. - /// - public static class App { - readonly static ComponentContainer _root = new ComponentContainer(); - - /// - /// The container for application level components. - /// - /// Pools of disposable objects can be placed here and they will be automatically - /// disposed when application domain is unloaded. - public static ICollection RootContainer { - get { return _root; } - } - - static App() { - AppDomain.CurrentDomain.DomainUnload += (sender, e) => _root.Dispose(); - } - } -} - diff --git a/Implab/Components/ComponentContainer.cs b/Implab/Components/ComponentContainer.cs deleted file mode 100644 --- a/Implab/Components/ComponentContainer.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Implab.Components { - /// - /// Component container, used to store track components in multi-threaded environmment. - /// - /// Instanses of this class are thread safe. - public class ComponentContainer : Disposable, ICollection { - List m_components = new List(); - readonly object m_lock = new object(); - - /// - /// Removes currently stored compoenents from the container and disposes them if possible. - /// - /// - /// A new components may be added before this method completes. - /// - public void Clear() { - List removed; - - lock (m_lock) { - removed = m_components; - m_components = new List(); - } - - foreach (var item in removed.OfType()) - item.Dispose(); - } - - /// - /// Checks whether the specified item in the collection. - /// - /// The item to check. - public bool Contains(T item) { - lock (m_lock) - return m_components.Contains(item); - } - - /// - /// Copies currently stored components to the specified array. - /// - /// A destination array for components. - /// A starting index in the destination array. - public void CopyTo(T[] array, int arrayIndex) { - lock (m_lock) - m_components.CopyTo(array, arrayIndex); - } - - /// - /// Remove the specified item from the collection. - /// - /// The item to remove. - public bool Remove(T item) { - lock (m_lock) - return m_components.Remove(item); - } - - /// - /// Gets the count of components in the collection. - /// - public int Count { - get { - lock (m_lock) - return m_components.Count; - } - } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// Always false. - /// - public bool IsReadOnly { - get { - return false; - } - } - - /// - /// Gets the enumerator for components in the collection. - /// - /// The enumerator. - public IEnumerator GetEnumerator() { - T[] items = new T[m_components.Count]; - lock (m_lock) { - m_components.CopyTo(items); - } - return (IEnumerator)items.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - - /// - /// Add the specified item to the collection. - /// - /// The item to add. - /// - /// If the collection is alredy disposed, the item isn't added to the collection and disposed if possible. - /// - 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); - } - - /// - /// Disposes the components stored in the collection. - /// - /// If set to true the collection is disposing. - protected override void Dispose(bool disposing) { - if (disposing) - Clear(); - - base.Dispose(disposing); - } - } -} - diff --git a/Implab/Components/DisposablePool.cs b/Implab/Components/DisposablePool.cs --- a/Implab/Components/DisposablePool.cs +++ b/Implab/Components/DisposablePool.cs @@ -6,10 +6,10 @@ using System.Diagnostics.CodeAnalysis; namespace Implab.Components { /// - /// The base class for implementing pools of disposable objects. + /// The base class for implementing thread-safe pools of disposable objects. /// /// - /// This class maintains a set of pre-created objects and which are frequently allocated and released + /// 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. /// Instances of this class are thread-safe. diff --git a/Implab/Components/IAsyncComponent.cs b/Implab/Components/IAsyncComponent.cs new file mode 100644 --- /dev/null +++ b/Implab/Components/IAsyncComponent.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; + +namespace Implab.Components { + /// + /// An interface for asynchronous components. + /// + /// + /// + /// Асинхронные компоненты не предназначены для одновременного использования несколькими клиентами, + /// однако существуют внутренние процессы, изменяющее состояние компонент без участия клиента. + /// Данный интерфейс определяет протокол взаимодействия с компонентой, при которм компоненте + /// посылаются сигналы от клиента, в ответ на которые компонента меняет свойство , + /// данное свойство содержит в себе новую задачу, выполняемую компонентой и данное свойство + /// может измениться только при получении нового сигнала от клиента. + /// + /// + /// В дополнение к компонента может определять другие свойства, в + /// которых будет передаваться информация о результате выполнения операции. + /// + /// + /// Особое внимание следует уделить реализации , который по своей природе + /// синхронный, данное правило безусловно можно нарушить, но тогда могут возникнуть проблемы с + /// тем, что ресурсы еще не освободились, а ход программы продолжается, что приведет к ошибкам, + /// например при попытке получить ресуср другим объектом, либо при заврешении программы. + /// + /// + /// + public interface IAsyncComponent { + /// + /// 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. + /// + Task Completion { get; } + } +} \ No newline at end of file diff --git a/Implab/Components/IRunnable.cs b/Implab/Components/IRunnable.cs --- a/Implab/Components/IRunnable.cs +++ b/Implab/Components/IRunnable.cs @@ -34,14 +34,7 @@ namespace Implab.Components { void Stop(CancellationToken ct); /// - /// 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. - /// - Task Completion { get; } - - /// - /// Current state of the componenet + /// Current state of the componenet, dynamically reflects the current state. /// ExecutionState State { get; } diff --git a/Implab/Components/RunnableComponent.cs b/Implab/Components/RunnableComponent.cs --- a/Implab/Components/RunnableComponent.cs +++ b/Implab/Components/RunnableComponent.cs @@ -12,7 +12,7 @@ namespace Implab.Components { /// This class provides a basic lifecycle from the creation to the /// termination of the component. /// - public class RunnableComponent : IRunnable, IInitializable, IDisposable { + public class RunnableComponent : IAsyncComponent, IRunnable, IInitializable, IDisposable { /// /// This class bounds lifetime to the task, @@ -80,7 +80,7 @@ namespace Implab.Components { } // 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 @@ namespace Implab.Components { // 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 @@ namespace Implab.Components { var cookie = new object(); if (MoveInitialize(cookie)) ScheduleTask(InitializeInternalAsync, CancellationToken.None, cookie); + else + throw new InvalidOperationException(); } /// @@ -171,6 +174,8 @@ namespace Implab.Components { 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 @@ namespace Implab.Components { var cookie = new object(); if (MoveStop(cookie)) ScheduleTask(StopAsync, ct, cookie); + else + throw new InvalidOperationException(); } async Task StopAsync(CancellationToken ct) { @@ -196,6 +203,16 @@ namespace Implab.Components { 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 @@ namespace Implab.Components { } } - + void ScheduleTask(Func next, CancellationToken ct, object cookie) { - protected async void ScheduleTask(Func 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