Mercurial > pub > ImplabNet
changeset 152:240aa6994018 v2
component model refactoring
author | cin |
---|---|
date | Thu, 11 Feb 2016 01:56:27 +0300 |
parents | ec91a6dfa5b3 |
children | b933ec88446e |
files | Implab/Component.cs Implab/ComponentContainer.cs Implab/Components/App.cs Implab/Components/ComponentContainer.cs Implab/Components/Disposable.cs Implab/Components/DisposablePool.cs Implab/Components/ExecutionState.cs Implab/Components/IInitializable.cs Implab/Components/IRunnable.cs Implab/Components/ObjectPool.cs Implab/Components/ServiceLocator.cs Implab/Diagnostics/ListenerBase.cs Implab/Disposable.cs Implab/DisposablePool.cs Implab/IComponentContainer.cs Implab/Implab.csproj Implab/ObjectPool.cs Implab/Parsing/Scanner.cs Implab/ServiceLocator.cs |
diffstat | 19 files changed, 667 insertions(+), 608 deletions(-) [+] |
line wrap: on
line diff
--- a/Implab/Component.cs Thu Feb 04 02:43:05 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Implab { - /// <summary> - /// Компоненты являются состовными объектами, имеют детерминированный период жизни, автоматически освобождают ресурсы входящие в них. - /// </summary> - /// <remarks>Компонента управляет временем жизни включенных в нее компонент</remarks> - public class Component: Disposable { - LinkedList<IDisposable> m_components = new LinkedList<IDisposable>(); - - /// <summary> - /// Коллекция компонент, из которых состоит текущая компонента. - /// </summary> - public ICollection<IDisposable> Components { - get { - AssertNotDisposed(); - return m_components; - } - } - - /// <summary> - /// Освобождает компоненты, входящие в состав текущей компоненты. - /// </summary> - /// <param name="disposing">Признак того, что происходит освобождение ресурсов.</param> - protected override void Dispose(bool disposing) { - if (disposing) { - foreach (var item in m_components) - item.Dispose(); - m_components.Clear(); - } - base.Dispose(disposing); - } - } -} \ No newline at end of file
--- a/Implab/ComponentContainer.cs Thu Feb 04 02:43:05 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -using System; -using Implab.Parallels; -using System.Threading; - -namespace Implab { - public class ComponentContainer : IComponentContainer, IDisposable { - static readonly ComponentContainer _appContainer; - - static ComponentContainer() { - _appContainer = new ComponentContainer(); - AppDomain.CurrentDomain.ProcessExit += HandleProcessExit; - } - - public static ComponentContainer Global { - get { - return _appContainer; - } - } - - bool m_disposed; - readonly AsyncQueue<IDisposable> m_components = new AsyncQueue<IDisposable>(); - - public void Add(IDisposable item) { - Safe.ArgumentNotNull(item, "item"); - Thread.MemoryBarrier(); - if (m_disposed) { - item.Dispose(); - } else { - m_components.Enqueue(item); - if (m_disposed && m_components.TryDequeue(out item)) - item.Dispose(); - } - } - - public void Dispose() { - m_disposed = true; - IDisposable item; - while (m_components.TryDequeue(out item)) - item.Dispose(); - } - - static void HandleProcessExit (object sender, EventArgs e) - { - _appContainer.Dispose(); - } - } -} -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/App.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,20 @@ +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>(); + + public static ICollection<object> RootContainer { + get { return _root; } + } + + static App() { + AppDomain.CurrentDomain.ProcessExit += (sender, e) => _root.Dispose(); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/ComponentContainer.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Implab.Components { + /// <summary> + /// Component container. + /// </summary> + /// <remarks>Instanses of this class are thread safe.</remarks> + public class ComponentContainer<T> : Disposable, ICollection<T> { + readonly HashSet<T> m_components = new HashSet<T>(); + + public void Clear() { + T[] removed; + + lock (m_components) { + removed = new T[m_components.Count]; + m_components.CopyTo(removed); + m_components.Clear(); + } + + foreach (var item in removed.OfType<IDisposable>()) + item.Dispose(); + } + + public bool Contains(T item) { + lock (m_components) + return m_components.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) { + lock (m_components) + m_components.CopyTo(array, arrayIndex); + } + + public bool Remove(T item) { + lock (m_components) + return m_components.Remove(item); + } + + public int Count { + get { + lock (m_components) + return m_components.Count; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + public IEnumerator<T> GetEnumerator() { + T[] items; + lock (m_components) { + items = new T[m_components.Count]; + m_components.CopyTo(items); + return (IEnumerator<T>)items.GetEnumerator(); + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + public void Add(T item) { + Safe.ArgumentNotNull(item, "item"); + + lock (m_components) { + if (IsDisposed) + Safe.Dispose(item); + else + m_components.Add(item); + } + } + + protected override void Dispose(bool disposing) { + base.Dispose(disposing); + Clear(); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/Disposable.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,103 @@ +using Implab.Diagnostics; +using System; +using System.Threading; + +namespace Implab.Components { + /// <summary> + /// Base class the objects which support disposing. + /// </summary> + public class Disposable : IDisposable { + + int m_disposed; + + public event EventHandler Disposed; + + public bool IsDisposed { + get { + Thread.MemoryBarrier(); + return m_disposed != 0; + } + } + + /// <summary> + /// Asserts the object is not disposed. + /// </summary> + /// <exception cref="ObjectDisposedException">The object is disposed</exception> + /// <remarks> + /// Успешная проверка того, что объект не освобожден еще не гарантирует, что он не + /// будет освобожден сразу после нее, поэтому методы использующие проверку должны + /// учитывать, что объект может быть освобожден из параллельного потока. + /// Данны метод служит для упрощения отладки ошибок при использовании объекта после его + /// освобождения. + /// </remarks> + /// <example> + /// // пример синхронизированного освобождения ресурсов + /// class FileStore : Disposable { + /// readonly TextWriter m_file; + /// readonly obejct m_sync = new object(); + /// + /// public FileStore(string file) { + /// m_file = new TextWriter(File.OpenWrite(file)); + /// } + /// + /// public void Write(string text) { + /// lock(m_sync) { + /// AssertNotDisposed(); + /// m_file.Write(text); + /// } + /// } + /// + /// protected override void Dispose(bool disposing) { + /// if (disposing) + /// lock(m_sync) { + /// m_file.Dipose(); + /// base.Dispose(true); + /// } + /// else + /// base.Dispose(false); + /// } + /// } + /// <example> + protected void AssertNotDisposed() { + Thread.MemoryBarrier(); + if (m_disposed != 0) + throw new ObjectDisposedException(ToString()); + } + /// <summary> + /// Вызывает событие <see cref="Disposed"/> + /// </summary> + /// <param name="disposing">Признак того, что нужно освободить ресурсы, иначе данный метод + /// вызван сборщиком мусора и нужно освобождать ТОЛЬКО неуправляемые ресурсы ТОЛЬКО этого + /// объекта.</param> + /// <remarks> + /// Данный метод вызывается гарантированно один раз даже при одновременном вызове <see cref="Dispose()"/> + /// из нескольких потоков. + /// </remarks> + protected virtual void Dispose(bool disposing) { + if (disposing) { + EventHandler temp = Disposed; + if (temp != null) + temp(this, EventArgs.Empty); + } + } + + public void Dispose() { + if (Interlocked.Increment(ref m_disposed) == 1) { + Dispose(true); + GC.SuppressFinalize(this); + } + } + + /// <summary> + /// Записывает сообщение об утечке объекта. + /// </summary> + protected virtual void ReportObjectLeaks() { + TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this); + } + + ~Disposable() { + Dispose(false); + ReportObjectLeaks(); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/DisposablePool.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,90 @@ +using System; +using Implab.Parallels; +using System.Threading; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace Implab { + public abstract class DisposablePool<T> : IDisposable { + readonly int m_size; + readonly AsyncQueue<T> m_queue = new AsyncQueue<T>(); + + [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] + static readonly bool _isValueType = typeof(T).IsValueType; + + bool m_disposed; + + int m_count; + + protected DisposablePool(int size) { + m_size = size; + } + + protected DisposablePool() : this(Environment.ProcessorCount+1) { + } + + public T Allocate() { + if (m_disposed) + throw new ObjectDisposedException(ToString()); + + T instance; + if (m_queue.TryDequeue(out instance)) { + Interlocked.Decrement(ref m_count); + } else { + instance = CreateInstance(); + Debug.Assert(!Object.Equals(instance, default(T)) || _isValueType); + } + return instance; + } + + protected abstract T CreateInstance(); + + protected virtual void CleanupInstance(T instance) { + } + + public void Release(T instance) { + if ( Object.Equals(instance,default(T)) && !_isValueType) + return; + + Thread.MemoryBarrier(); + if (m_count < m_size && !m_disposed) { + Interlocked.Increment(ref m_count); + + CleanupInstance(instance); + + m_queue.Enqueue(instance); + + // пока элемент возвращался в кеш, была начата операция освобождения всего кеша + // и возможно уже законцена, в таком случае следует извлечь элемент обратно и + // освободить его. Если операция освобождения кеша еще не заврешилась, то будет + // изъят и освобожден произвольный элемен, что не повлияет на ход всего процесса. + if (m_disposed && m_queue.TryDequeue(out instance) && instance is IDisposable) + ((IDisposable)instance).Dispose() ; + + } else { + if (instance is IDisposable) + ((IDisposable)instance).Dispose(); + } + } + + protected virtual void Dispose(bool disposing) { + if (disposing) { + m_disposed = true; + T instance; + while (m_queue.TryDequeue(out instance)) + if (instance is IDisposable) + ((IDisposable)instance).Dispose(); + } + } + + #region IDisposable implementation + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/ExecutionState.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,12 @@ +namespace Implab.Components { + public enum ExecutionState { + Uninitialized, + Initial, + Starting, + Running, + Stopping, + Stopped, + Disposed, + Failed + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/IInitializable.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,21 @@ +using System; + +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()"/> + /// </summary> + public interface IInitializable { + /// <summary> + /// Completes initialization. + /// </summary> + /// <remarks> + /// Normally virtual 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 + /// of components where cyclic references may take place. + /// </remarks> + void Init(); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/IRunnable.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,14 @@ +using System; + +namespace Implab.Components { + public interface IRunnable { + IPromise Start(); + + IPromise Stop(); + + ExecutionState State { get; } + + Exception LastError { get; } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/ObjectPool.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,60 @@ +using Implab.Parallels; +using System; +using System.Threading; + +namespace Implab.Components { + /// <summary> + /// Базовый класс для создания пулов объектов. + /// </summary> + /// <remarks> + /// <para>Пул объектов позволяет многократно использовать один и тотже объект, + /// что актуально для объектов, создание которых требует существенных ресурсов. + /// Пул объектов использует слабые ссылки, чтобы не препятствовать освобождению + /// ресурсов и создает новые объекты при необходимости.</para> + /// <para> + /// Наследники должны реализовывать метод <see cref="CreateInstance()"/> для создания + /// новых экземпляров. + /// </para> + /// <para>Пул поддерживает обращения сразу из нескольких потоков.</para> + /// </remarks> + public abstract class ObjectPool<T> where T : class { + readonly AsyncQueue<WeakReference> m_queue = new AsyncQueue<WeakReference>(); + readonly int m_size; + int m_count = 0; + + protected ObjectPool() : this(Environment.ProcessorCount+1) { + + } + + protected ObjectPool(int size) { + Safe.ArgumentInRange(size,1,size,"size"); + + m_size = size; + } + + protected abstract T CreateInstance(); + + protected virtual void CleanupInstance(T instance) { + } + + public T Allocate() { + WeakReference reference; + while (m_queue.TryDequeue(out reference)) { + Interlocked.Decrement(ref m_count); + object instance = reference.Target; + if (instance == null) + continue; + return (T)instance; + } + return CreateInstance(); + } + + public void Release(T instance) { + if (m_count < m_size && instance != null) { + Interlocked.Increment(ref m_count); + CleanupInstance(instance); + m_queue.Enqueue(new WeakReference(instance)); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Components/ServiceLocator.cs Thu Feb 11 01:56:27 2016 +0300 @@ -0,0 +1,248 @@ +using System; +using System.Collections.Generic; + +namespace Implab.Components { + /// <summary> + /// Коллекция сервисов, позволяет регистрировать и получать сервисы. + /// </summary> + public class ServiceLocator: Disposable, IServiceLocator, IServiceProvider { + // запись о сервисе + struct ServiceEntry : IDisposable { + public object service; // сервис + public bool shared; // признак того, что сервис НЕ нужно освобождать + public Func<object> activator; // активатор сервиса при первом обращении + public Action<object> cleanup; // функция для очистки сервиса + public List<Type> associated; // ссылки на текущую запись + public Type origin; // ссылка на оригинальную запись о сервисе + + #region IDisposable implementation + + public void Dispose() { + if (shared) + return; + if (cleanup != null) { + if (service != null) + cleanup(service); + } else + Safe.Dispose(service); + } + + #endregion + } + + // словарь существующих сервисов + readonly Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>(); + + /// <summary> + /// Получает объект предоставляющий сервис <typeparamref name="T"/>. + /// </summary> + /// <typeparam name="T">Тип запрашиваемого сервиса</typeparam> + /// <returns>Объект, реализующий сервис</returns> + /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> + public T GetService<T>() { + object result; + if (TryGetService(typeof(T), out result)) + return (T)result; + throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, typeof(T))); + } + + + /// <summary> + /// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис + /// не возникает исключений. + /// </summary> + /// <typeparam name="T">Тип требуемого сервиса.</typeparam> + /// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param> + /// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns> + public bool TryGetService<T>(out T service) { + object result; + if (TryGetService(typeof(T), out result)) { + service = (T)result; + return true; + } + service = default(T); + return false; + } + + /// <summary> + /// Получает объект предоставляющий сервис <paramref name="serviceType"/> + /// </summary> + /// <param name="serviceType">Тип запрашиваемого сервиса</param> + /// <returns>Объект, реализующий сервис</returns> + /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> + public object GetService(Type serviceType) { + object result; + if (TryGetService(serviceType, out result)) + return result; + throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, serviceType)); + } + + /// <summary> + /// Пытается получить требуемый сервис или совместимый с ним. + /// </summary> + /// <returns><c>true</c>, если сервис был найден, <c>false</c> в противном случае..</returns> + /// <param name="serviceType">Тип запрашиваемого сервиса.</param> + /// <param name="service">Искомый сервис.</param> + public virtual bool TryGetService(Type serviceType, out object service) { + Safe.ArgumentNotNull(serviceType, "serviceType"); + AssertNotDisposed(); + + ServiceEntry se; + if (!m_services.TryGetValue(serviceType, out se)) { + // ищем ближайщий объект, реализующий нужный сервис + Type pt = null; + foreach (var t in m_services.Keys) + if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt))) + pt = t; + + if (pt == null) { + // нет нужного сервиса + service = null; + return false; + } + + var pe = m_services[pt]; + + // найденная запись может ссылаться на оригинальную запись с сервисом + if(pe.origin != null) { + pt = pe.origin; + pe = m_services[pt]; + } + + // добавляем список с обратными ссылками + if (pe.associated == null) + pe.associated = new List<Type>(); + + pe.associated.Add(serviceType); + + // обновляем родительскую запись + m_services[pt] = pe; + + // создаем запись со ссылкой + se = new ServiceEntry { + service = pe.service, + origin = pt, + shared = true // предотвращаем множественные попытки освобождения + }; + + m_services[serviceType] = se; + } + + // запись содержит в себе информацию о сервисе + if (se.service != null) { + service = se.service; + return true; + } + + // текущая запись является ссылкой + if (se.origin != null) { + se.service = GetService(se.origin); + m_services[serviceType] = se; + service = se.service; + return true; + } + + // текущая запись не является ссылкой и не имеет информации о сервисе + // она должна сожержать информацию об активации + if (se.activator != null) { + se.service = se.activator(); + + m_services[serviceType] = se; + + service = se.service; + return true; + } + + service = null; + return false; + } + + /// <summary> + /// Регистрирует фабрику для активации сервиса по первому требованию. + /// </summary> + /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> + /// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param> + /// <param name = "cleanup">Метод для освобождения экземпляра сервиса, будет вызыван при освобождении сервис-локатора.</param> + /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks> + public void Register<T>(Func<T> activator, Action<T> cleanup) { + Safe.ArgumentNotNull(activator, "activator"); + + AssertNotDisposed(); + + Unregister(typeof(T)); + + var serviceEntry = new ServiceEntry(); + serviceEntry.activator = () => activator(); + if (cleanup != null) + serviceEntry.cleanup = instance => cleanup((T)instance); + m_services[typeof(T)] = serviceEntry; + } + + public void Register<T>(Func<T> activator) { + Register(activator, null); + } + + /// <summary> + /// Регистрирует объект, предоставляющий сервис. + /// </summary> + /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> + /// <param name="service">Объект, предоставляющий сервис.</param> + /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception> + /// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks> + public void Register<T>(T service) { + Register(service, true); + } + + /// <summary> + /// Регистрирует объект, предоставляющий сервис. Повторная регистрация отменяет уже существующую. + /// </summary> + /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> + /// <param name="service">Объект, предоставляющий сервис.</param> + /// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param> + public void Register<T>(T service, bool shared) { + Safe.ArgumentNotNull(service, "service"); + + AssertNotDisposed(); + + Unregister(typeof(T)); + + m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared }; + } + + public void Unregister(Type serviceType) { + Safe.ArgumentNotNull(serviceType, "serviceType"); + + AssertNotDisposed(); + + ServiceEntry se; + if (m_services.TryGetValue(serviceType, out se)) { + if (se.origin != null) { + var pe = m_services[se.origin]; + pe.associated.Remove(serviceType); + } + // освобождаем ресурсы + se.Dispose(); + m_services.Remove(serviceType); + + // убираем связанные записи + if (se.associated != null) + foreach (var item in se.associated) + m_services.Remove(item); + } + } + + /// <summary> + /// Освобождает зарегистрированные сервисы (которые требуется освобоить). + /// </summary> + /// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param> + protected override void Dispose(bool disposing) { + if (disposing) { + + foreach (var entry in m_services.Values) + entry.Dispose(); + + } + base.Dispose(disposing); + } + } +} \ No newline at end of file
--- a/Implab/Diagnostics/ListenerBase.cs Thu Feb 04 02:43:05 2016 +0300 +++ b/Implab/Diagnostics/ListenerBase.cs Thu Feb 11 01:56:27 2016 +0300 @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; +using Implab.Components; namespace Implab.Diagnostics { public abstract class ListenerBase : ServiceLocator, ILogWriter<object>, ILogWriter<TraceEvent> {
--- a/Implab/Disposable.cs Thu Feb 04 02:43:05 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -using Implab.Diagnostics; -using System; -using System.Threading; -using System.IO; - -namespace Implab { - /// <summary> - /// Объект, поддерживающий освобождение ресурсов. - /// </summary> - public class Disposable : IDisposable { - - int m_disposed; - - public event EventHandler Disposed; - - public bool IsDisposed { - get { - Thread.MemoryBarrier(); - return m_disposed != 0; - } - } - - /// <summary> - /// Asserts the object is not disposed. - /// </summary> - /// <exception cref="ObjectDisposedException">The object is disposed</exception> - /// <remarks> - /// Успешная проверка того, что объект не освобожден еще не гарантирует, что он не - /// будет освобожден сразу после нее, поэтому методы использующие проверку должны - /// учитывать, что объект может быть освобожден из параллельного потока. - /// Данны метод служит для упрощения отладки ошибок при использовании объекта после его - /// освобождения. - /// </remarks> - /// <example> - /// // пример синхронизированного освобождения ресурсов - /// class FileStore : Disposable { - /// readonly TextWriter m_file; - /// readonly obejct m_sync = new object(); - /// - /// public FileStore(string file) { - /// m_file = new TextWriter(File.OpenWrite(file)); - /// } - /// - /// public void Write(string text) { - /// lock(m_sync) { - /// AssertNotDisposed(); - /// m_file.Write(text); - /// } - /// } - /// - /// protected override void Dispose(bool disposing) { - /// if (disposing) - /// lock(m_sync) { - /// m_file.Dipose(); - /// base.Dispose(true); - /// } - /// else - /// base.Dispose(false); - /// } - /// } - /// <example> - protected void AssertNotDisposed() { - Thread.MemoryBarrier(); - if (m_disposed != 0) - throw new ObjectDisposedException(ToString()); - } - /// <summary> - /// Вызывает событие <see cref="Disposed"/> - /// </summary> - /// <param name="disposing">Признак того, что нужно освободить ресурсы, иначе данный метод - /// вызван сборщиком мусора и нужно освобождать ТОЛЬКО неуправляемые ресурсы ТОЛЬКО этого - /// объекта.</param> - /// <remarks> - /// Данный метод вызывается гарантированно один раз даже при одновременном вызове <see cref="Dispose()"/> - /// из нескольких потоков. - /// </remarks> - protected virtual void Dispose(bool disposing) { - if (disposing) { - EventHandler temp = Disposed; - if (temp != null) - temp(this, EventArgs.Empty); - } - } - - public void Dispose() { - if (Interlocked.Increment(ref m_disposed) == 1) { - Dispose(true); - GC.SuppressFinalize(this); - } - } - - /// <summary> - /// Записывает сообщение об утечке объекта. - /// </summary> - protected virtual void ReportObjectLeaks() { - TraceLog.TraceWarning("The object is marked as disposable but isn't disposed properly: {0}", this); - } - - ~Disposable() { - Dispose(false); - ReportObjectLeaks(); - } - } -} \ No newline at end of file
--- a/Implab/DisposablePool.cs Thu Feb 04 02:43:05 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -using System; -using Implab.Parallels; -using System.Threading; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Implab { - public abstract class DisposablePool<T> : IDisposable { - readonly int m_size; - readonly AsyncQueue<T> m_queue = new AsyncQueue<T>(); - - [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] - static readonly bool _isValueType = typeof(T).IsValueType; - - bool m_disposed; - - int m_count; - - protected DisposablePool(int size) { - m_size = size; - } - - protected DisposablePool() : this(Environment.ProcessorCount+1) { - } - - public T Allocate() { - if (m_disposed) - throw new ObjectDisposedException(ToString()); - - T instance; - if (m_queue.TryDequeue(out instance)) { - Interlocked.Decrement(ref m_count); - } else { - instance = CreateInstance(); - Debug.Assert(!Object.Equals(instance, default(T)) || _isValueType); - } - return instance; - } - - protected abstract T CreateInstance(); - - protected virtual void CleanupInstance(T instance) { - } - - public void Release(T instance) { - if ( Object.Equals(instance,default(T)) && !_isValueType) - return; - - Thread.MemoryBarrier(); - if (m_count < m_size && !m_disposed) { - Interlocked.Increment(ref m_count); - - CleanupInstance(instance); - - m_queue.Enqueue(instance); - - // пока элемент возвращался в кеш, была начата операция освобождения всего кеша - // и возможно уже законцена, в таком случае следует извлечь элемент обратно и - // освободить его. Если операция освобождения кеша еще не заврешилась, то будет - // изъят и освобожден произвольный элемен, что не повлияет на ход всего процесса. - if (m_disposed && m_queue.TryDequeue(out instance) && instance is IDisposable) - ((IDisposable)instance).Dispose() ; - - } else { - if (instance is IDisposable) - ((IDisposable)instance).Dispose(); - } - } - - protected virtual void Dispose(bool disposing) { - if (disposing) { - m_disposed = true; - T instance; - while (m_queue.TryDequeue(out instance)) - if (instance is IDisposable) - ((IDisposable)instance).Dispose(); - } - } - - #region IDisposable implementation - - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } - - #endregion - } -} -
--- a/Implab/IComponentContainer.cs Thu Feb 04 02:43:05 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -using System; - -namespace Implab { - public interface IComponentContainer { - void Add(IDisposable component); - } -} -
--- a/Implab/Implab.csproj Thu Feb 04 02:43:05 2016 +0300 +++ b/Implab/Implab.csproj Thu Feb 11 01:56:27 2016 +0300 @@ -75,7 +75,6 @@ <Reference Include="mscorlib" /> </ItemGroup> <ItemGroup> - <Compile Include="Component.cs" /> <Compile Include="CustomEqualityComparer.cs" /> <Compile Include="Diagnostics\ConsoleTraceListener.cs" /> <Compile Include="Diagnostics\EventText.cs" /> @@ -85,7 +84,6 @@ <Compile Include="Diagnostics\TraceLog.cs" /> <Compile Include="Diagnostics\TraceEvent.cs" /> <Compile Include="Diagnostics\TraceEventType.cs" /> - <Compile Include="Disposable.cs" /> <Compile Include="ICancellable.cs" /> <Compile Include="IProgressHandler.cs" /> <Compile Include="IProgressNotifier.cs" /> @@ -130,8 +128,6 @@ <Compile Include="Parsing\StarToken.cs" /> <Compile Include="Parsing\SymbolToken.cs" /> <Compile Include="Parsing\Token.cs" /> - <Compile Include="ServiceLocator.cs" /> - <Compile Include="TaskController.cs" /> <Compile Include="ProgressInitEventArgs.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Parallels\AsyncPool.cs" /> @@ -144,11 +140,7 @@ <Compile Include="Diagnostics\LogEventArgs.cs" /> <Compile Include="Diagnostics\LogEventArgsT.cs" /> <Compile Include="Diagnostics\Extensions.cs" /> - <Compile Include="IComponentContainer.cs" /> <Compile Include="PromiseEventType.cs" /> - <Compile Include="ComponentContainer.cs" /> - <Compile Include="DisposablePool.cs" /> - <Compile Include="ObjectPool.cs" /> <Compile Include="Parallels\AsyncQueue.cs" /> <Compile Include="PromiseT.cs" /> <Compile Include="IDeferred.cs" /> @@ -180,6 +172,16 @@ <Compile Include="SuccessPromiseT.cs" /> <Compile Include="PromiseAwaiterT.cs" /> <Compile Include="PromiseAwaiter.cs" /> + <Compile Include="Components\ComponentContainer.cs" /> + <Compile Include="Components\Disposable.cs" /> + <Compile Include="Components\DisposablePool.cs" /> + <Compile Include="Components\ObjectPool.cs" /> + <Compile Include="Components\ServiceLocator.cs" /> + <Compile Include="Components\IInitializable.cs" /> + <Compile Include="TaskController.cs" /> + <Compile Include="Components\App.cs" /> + <Compile Include="Components\IRunnable.cs" /> + <Compile Include="Components\ExecutionState.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup /> @@ -252,4 +254,7 @@ </Properties> </MonoDevelop> </ProjectExtensions> + <ItemGroup> + <Folder Include="Components\" /> + </ItemGroup> </Project> \ No newline at end of file
--- a/Implab/ObjectPool.cs Thu Feb 04 02:43:05 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -using Implab.Parallels; -using System; -using System.Threading; - -namespace Implab { - /// <summary> - /// Базовый класс для создания пулов объектов. - /// </summary> - /// <remarks> - /// <para>Пул объектов позволяет многократно использовать один и тотже объект, - /// что актуально для объектов, создание которых требует существенных ресурсов. - /// Пул объектов использует слабые ссылки, чтобы не препятствовать освобождению - /// ресурсов и создает новые объекты при необходимости.</para> - /// <para> - /// Наследники должны реализовывать метод <see cref="CreateInstance()"/> для создания - /// новых экземпляров. - /// </para> - /// <para>Пул поддерживает обращения сразу из нескольких потоков.</para> - /// </remarks> - public abstract class ObjectPool<T> where T : class { - readonly AsyncQueue<WeakReference> m_queue = new AsyncQueue<WeakReference>(); - readonly int m_size; - int m_count = 0; - - protected ObjectPool() : this(Environment.ProcessorCount+1) { - - } - - protected ObjectPool(int size) { - Safe.ArgumentInRange(size,1,size,"size"); - - m_size = size; - } - - protected abstract T CreateInstance(); - - protected virtual void CleanupInstance(T instance) { - } - - public T Allocate() { - WeakReference reference; - while (m_queue.TryDequeue(out reference)) { - Interlocked.Decrement(ref m_count); - object instance = reference.Target; - if (instance == null) - continue; - return (T)instance; - } - return CreateInstance(); - } - - public void Release(T instance) { - if (m_count < m_size && instance != null) { - Interlocked.Increment(ref m_count); - CleanupInstance(instance); - m_queue.Enqueue(new WeakReference(instance)); - } - } - } -}
--- a/Implab/Parsing/Scanner.cs Thu Feb 04 02:43:05 2016 +0300 +++ b/Implab/Parsing/Scanner.cs Thu Feb 11 01:56:27 2016 +0300 @@ -2,9 +2,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Implab.Components; namespace Implab.Parsing { /// <summary>
--- a/Implab/ServiceLocator.cs Thu Feb 04 02:43:05 2016 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,248 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Implab { - /// <summary> - /// Коллекция сервисов, позволяет регистрировать и получать сервисы. - /// </summary> - public class ServiceLocator: Disposable, IServiceLocator, IServiceProvider { - // запись о сервисе - struct ServiceEntry : IDisposable { - public object service; // сервис - public bool shared; // признак того, что сервис НЕ нужно освобождать - public Func<object> activator; // активатор сервиса при первом обращении - public Action<object> cleanup; // функция для очистки сервиса - public List<Type> associated; // ссылки на текущую запись - public Type origin; // ссылка на оригинальную запись о сервисе - - #region IDisposable implementation - - public void Dispose() { - if (shared) - return; - if (cleanup != null) { - if (service != null) - cleanup(service); - } else - Safe.Dispose(service); - } - - #endregion - } - - // словарь существующих сервисов - readonly Dictionary<Type, ServiceEntry> m_services = new Dictionary<Type,ServiceEntry>(); - - /// <summary> - /// Получает объект предоставляющий сервис <typeparamref name="T"/>. - /// </summary> - /// <typeparam name="T">Тип запрашиваемого сервиса</typeparam> - /// <returns>Объект, реализующий сервис</returns> - /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> - public T GetService<T>() { - object result; - if (TryGetService(typeof(T), out result)) - return (T)result; - throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, typeof(T))); - } - - - /// <summary> - /// Пытается получить указанный сервис, в случае, если компонента не предоставляет требуемый сервис - /// не возникает исключений. - /// </summary> - /// <typeparam name="T">Тип требуемого сервиса.</typeparam> - /// <param name="service">Объект реализующий сервис, или <c>default(T)</c> если такового нет.</param> - /// <returns><c>true</c> - сервис найден, <c>false</c> - сервис не зарегистрирован.</returns> - public bool TryGetService<T>(out T service) { - object result; - if (TryGetService(typeof(T), out result)) { - service = (T)result; - return true; - } - service = default(T); - return false; - } - - /// <summary> - /// Получает объект предоставляющий сервис <paramref name="serviceType"/> - /// </summary> - /// <param name="serviceType">Тип запрашиваемого сервиса</param> - /// <returns>Объект, реализующий сервис</returns> - /// <exception cref="KeyNotFoundException">Сервис не зарегистрирован</exception> - public object GetService(Type serviceType) { - object result; - if (TryGetService(serviceType, out result)) - return result; - throw new ApplicationException (String.Format ("{0} doesn't provide {1} service", this, serviceType)); - } - - /// <summary> - /// Пытается получить требуемый сервис или совместимый с ним. - /// </summary> - /// <returns><c>true</c>, если сервис был найден, <c>false</c> в противном случае..</returns> - /// <param name="serviceType">Тип запрашиваемого сервиса.</param> - /// <param name="service">Искомый сервис.</param> - public virtual bool TryGetService(Type serviceType, out object service) { - Safe.ArgumentNotNull(serviceType, "serviceType"); - AssertNotDisposed(); - - ServiceEntry se; - if (!m_services.TryGetValue(serviceType, out se)) { - // ищем ближайщий объект, реализующий нужный сервис - Type pt = null; - foreach (var t in m_services.Keys) - if (serviceType.IsAssignableFrom(t) && (pt == null || t.IsAssignableFrom(pt))) - pt = t; - - if (pt == null) { - // нет нужного сервиса - service = null; - return false; - } - - var pe = m_services[pt]; - - // найденная запись может ссылаться на оригинальную запись с сервисом - if(pe.origin != null) { - pt = pe.origin; - pe = m_services[pt]; - } - - // добавляем список с обратными ссылками - if (pe.associated == null) - pe.associated = new List<Type>(); - - pe.associated.Add(serviceType); - - // обновляем родительскую запись - m_services[pt] = pe; - - // создаем запись со ссылкой - se = new ServiceEntry { - service = pe.service, - origin = pt, - shared = true // предотвращаем множественные попытки освобождения - }; - - m_services[serviceType] = se; - } - - // запись содержит в себе информацию о сервисе - if (se.service != null) { - service = se.service; - return true; - } - - // текущая запись является ссылкой - if (se.origin != null) { - se.service = GetService(se.origin); - m_services[serviceType] = se; - service = se.service; - return true; - } - - // текущая запись не является ссылкой и не имеет информации о сервисе - // она должна сожержать информацию об активации - if (se.activator != null) { - se.service = se.activator(); - - m_services[serviceType] = se; - - service = se.service; - return true; - } - - service = null; - return false; - } - - /// <summary> - /// Регистрирует фабрику для активации сервиса по первому требованию. - /// </summary> - /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> - /// <param name="activator">Фабрика для создания/получения объекта, предоставляющего сервис.</param> - /// <param name = "cleanup">Метод для освобождения экземпляра сервиса, будет вызыван при освобождении сервис-локатора.</param> - /// <remarks>При освобождении сервис-локатора, сервисы полученные в результате активации также будут освобождены.</remarks> - public void Register<T>(Func<T> activator, Action<T> cleanup) { - Safe.ArgumentNotNull(activator, "activator"); - - AssertNotDisposed(); - - Unregister(typeof(T)); - - var serviceEntry = new ServiceEntry(); - serviceEntry.activator = () => activator(); - if (cleanup != null) - serviceEntry.cleanup = instance => cleanup((T)instance); - m_services[typeof(T)] = serviceEntry; - } - - public void Register<T>(Func<T> activator) { - Register(activator, null); - } - - /// <summary> - /// Регистрирует объект, предоставляющий сервис. - /// </summary> - /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> - /// <param name="service">Объект, предоставляющий сервис.</param> - /// <exception cref="InvalidOperationException">Указанный сервис уже зарегистрирован.</exception> - /// <remarks>Сервис-локатором не управляет временем жизни объекта для зарегистрированного сервиса.</remarks> - public void Register<T>(T service) { - Register(service, true); - } - - /// <summary> - /// Регистрирует объект, предоставляющий сервис. Повторная регистрация отменяет уже существующую. - /// </summary> - /// <typeparam name="T">Тип регистрируемого сервиса.</typeparam> - /// <param name="service">Объект, предоставляющий сервис.</param> - /// <param name="shared">Признак того, что объект является разделяемым и сервис-локатор не должен его освобождать.</param> - public void Register<T>(T service, bool shared) { - Safe.ArgumentNotNull(service, "service"); - - AssertNotDisposed(); - - Unregister(typeof(T)); - - m_services[typeof(T)] = new ServiceEntry { service = service, shared = shared }; - } - - public void Unregister(Type serviceType) { - Safe.ArgumentNotNull(serviceType, "serviceType"); - - AssertNotDisposed(); - - ServiceEntry se; - if (m_services.TryGetValue(serviceType, out se)) { - if (se.origin != null) { - var pe = m_services[se.origin]; - pe.associated.Remove(serviceType); - } - // освобождаем ресурсы - se.Dispose(); - m_services.Remove(serviceType); - - // убираем связанные записи - if (se.associated != null) - foreach (var item in se.associated) - m_services.Remove(item); - } - } - - /// <summary> - /// Освобождает зарегистрированные сервисы (которые требуется освобоить). - /// </summary> - /// <param name="disposing">Призанак того, что нужно освободить ресурсы.</param> - protected override void Dispose(bool disposing) { - if (disposing) { - - foreach (var entry in m_services.Values) - entry.Dispose(); - - } - base.Dispose(disposing); - } - } -} \ No newline at end of file