From 11bc7cb60c385b79df8d386b9ea01ebd91ce5b8a Mon Sep 17 00:00:00 2001 From: Westin Miller Date: Thu, 9 Nov 2017 09:42:48 -0800 Subject: [PATCH] Trace level messages Collection change events are all deferred, and replaced with reset if it makes sense. --- Torch.Server/Managers/EntityControlManager.cs | 4 +- Torch/Collections/MTObservableCollection.cs | 147 +++++++++++++----- Torch/Collections/MtObservableList.cs | 60 ++++++- 3 files changed, 163 insertions(+), 48 deletions(-) diff --git a/Torch.Server/Managers/EntityControlManager.cs b/Torch.Server/Managers/EntityControlManager.cs index 5f43884..65d1e8f 100644 --- a/Torch.Server/Managers/EntityControlManager.cs +++ b/Torch.Server/Managers/EntityControlManager.cs @@ -77,7 +77,7 @@ namespace Torch.Server.Managers if (evm is T m) { var result = _factory(m); - _log.Debug($"Model factory {_factory.Method} created {result} for {evm}"); + _log.Trace($"Model factory {_factory.Method} created {result} for {evm}"); return result; } return null; @@ -224,7 +224,7 @@ namespace Torch.Server.Managers if (factory.Method.GetParameters()[0].ParameterType.IsInstanceOfType(model) && factory.DynamicInvoke(model) is Control result) { - _log.Debug($"Control factory {factory.Method} created {result}"); + _log.Trace($"Control factory {factory.Method} created {result}"); return result; } _log.Warn($"No control created for {model}"); diff --git a/Torch/Collections/MTObservableCollection.cs b/Torch/Collections/MTObservableCollection.cs index dec4b60..3096bea 100644 --- a/Torch/Collections/MTObservableCollection.cs +++ b/Torch/Collections/MTObservableCollection.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -16,7 +17,8 @@ namespace Torch.Collections /// /// Collection type /// Value type - public abstract class MtObservableCollection : INotifyPropertyChanged, INotifyCollectionChanged, IEnumerable where TC : class, ICollection + public abstract class MtObservableCollection : INotifyPropertyChanged, INotifyCollectionChanged, + IEnumerable, ICollection where TC : class, ICollection { protected readonly ReaderWriterLockSlim Lock; protected readonly TC Backing; @@ -30,6 +32,13 @@ namespace Torch.Collections Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); _version = 0; _threadViews = new ThreadLocal(() => new ThreadView(this)); + _deferredSnapshot = new DeferredUpdateToken(this); + _flushEventQueue = new Timer(FlushCollectionEventQueue); + } + + ~MtObservableCollection() + { + _flushEventQueue.Dispose(); } /// @@ -53,6 +62,7 @@ namespace Torch.Collections } #region ICollection + /// public void Add(TV item) { @@ -61,7 +71,8 @@ namespace Torch.Collections Backing.Add(item); MarkSnapshotsDirty(); OnPropertyChanged(nameof(Count)); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, Backing.Count - 1)); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, + Backing.Count - 1)); } } @@ -107,7 +118,8 @@ namespace Torch.Collections OnPropertyChanged(nameof(Count)); OnCollectionChanged(oldIndex.HasValue - ? new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, oldIndex.Value) + ? new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, + oldIndex.Value) : new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); return true; } @@ -126,11 +138,13 @@ namespace Torch.Collections /// public bool IsReadOnly => Backing.IsReadOnly; + #endregion #region Event Wrappers - private readonly WeakReference _deferredSnapshot = new WeakReference(null); - private bool _deferredSnapshotTaken = false; + + private readonly DeferredUpdateToken _deferredSnapshot; + /// /// Disposable that stops update signals and signals a full refresh when disposed. /// @@ -138,13 +152,8 @@ namespace Torch.Collections { using (Lock.WriteUsing()) { - if (_deferredSnapshotTaken) - return new DummyToken(); - DeferredUpdateToken token; - if (!_deferredSnapshot.TryGetTarget(out token)) - _deferredSnapshot.SetTarget(token = new DeferredUpdateToken()); - token.SetCollection(this); - return token; + _deferredSnapshot.Enter(); + return _deferredSnapshot; } } @@ -157,53 +166,81 @@ namespace Torch.Collections private class DeferredUpdateToken : IDisposable { - private MtObservableCollection _collection; + private readonly MtObservableCollection _collection; + private int _depth; - internal void SetCollection(MtObservableCollection c) + internal DeferredUpdateToken(MtObservableCollection c) { - c._deferredSnapshotTaken = true; _collection = c; - c.NotificationsEnabled = false; + } + + internal void Enter() + { + if (Interlocked.Increment(ref _depth) == 1) + { + _collection.NotificationsEnabled = false; + } } public void Dispose() { - using (_collection.Lock.WriteUsing()) - { - _collection.NotificationsEnabled = true; - _collection.OnPropertyChanged(nameof(Count)); - _collection.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - _collection._deferredSnapshotTaken = false; - } - + if (Interlocked.Decrement(ref _depth) == 0) + using (_collection.Lock.WriteUsing()) + { + _collection.NotificationsEnabled = true; + _collection.OnPropertyChanged(nameof(Count)); + _collection.OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } } } protected void OnPropertyChanged(string propName) { - NotifyEvent(this, new PropertyChangedEventArgs(propName)); + if (!NotificationsEnabled) + return; + _propertyChangedEvent.Raise(this, new PropertyChangedEventArgs(propName)); } protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { - NotifyEvent(this, e); - OnPropertyChanged("Item[]"); + if (!NotificationsEnabled) + return; + _collectionEventQueue.Enqueue(e); + // In half a second, flush the events + _flushEventQueue.Change(500, -1); } - protected void NotifyEvent(object sender, PropertyChangedEventArgs args) + private readonly Timer _flushEventQueue; + + private readonly Queue _collectionEventQueue = + new Queue(); + + private void FlushCollectionEventQueue(object data) { - if (NotificationsEnabled) - _propertyChangedEvent.Raise(sender, args); + bool reset = _collectionEventQueue.Count >= 2; + var itemsChanged = false; + while (_collectionEventQueue.TryDequeue(out NotifyCollectionChangedEventArgs e)) + if (!reset) + { + _collectionChangedEvent.Raise(this, e); + itemsChanged = true; + } + + if (reset) + { + _collectionChangedEvent.Raise(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + itemsChanged = true; + } + + if (itemsChanged) + OnPropertyChanged("Item[]"); } - protected void NotifyEvent(object sender, NotifyCollectionChangedEventArgs args) - { - if (NotificationsEnabled) - _collectionChangedEvent.Raise(sender, args); - } - - private readonly MtObservableEvent _propertyChangedEvent = + private readonly MtObservableEvent _propertyChangedEvent + = new MtObservableEvent(); + /// public event PropertyChangedEventHandler PropertyChanged { @@ -219,8 +256,10 @@ namespace Torch.Collections } } - private readonly MtObservableEvent _collectionChangedEvent = - new MtObservableEvent(); + private readonly MtObservableEvent + _collectionChangedEvent = + new MtObservableEvent(); + /// public event NotifyCollectionChangedEventHandler CollectionChanged { @@ -235,14 +274,16 @@ namespace Torch.Collections OnPropertyChanged(nameof(IsObserved)); } } + #endregion - + /// /// Is this collection observed by any listeners. /// public bool IsObserved => _collectionChangedEvent.IsObserved || _propertyChangedEvent.IsObserved; #region Enumeration + /// /// Manages a snapshot to a collection and dispatches enumerators from that snapshot. /// @@ -250,10 +291,12 @@ namespace Torch.Collections { private readonly MtObservableCollection _owner; private readonly WeakReference> _snapshot; + /// /// The of the /// private int _snapshotVersion; + /// /// Number of strong references to the value pointed to be /// @@ -358,6 +401,28 @@ namespace Torch.Collections /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + #endregion + + /// + void ICollection.CopyTo(Array array, int index) + { + using (Lock.ReadUsing()) + { + int i = index; + foreach (TV value in Backing) + { + if (i >= array.Length) + break; + array.SetValue(value, i++); + } + } + } + + /// + object ICollection.SyncRoot => this; + + /// + bool ICollection.IsSynchronized => true; } -} +} \ No newline at end of file diff --git a/Torch/Collections/MtObservableList.cs b/Torch/Collections/MtObservableList.cs index b290d03..af21eb0 100644 --- a/Torch/Collections/MtObservableList.cs +++ b/Torch/Collections/MtObservableList.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; @@ -12,7 +13,7 @@ namespace Torch.Collections /// Multithread safe, observable list /// /// Value type - public class MtObservableList : MtObservableCollection, T>, IList + public class MtObservableList : MtObservableCollection, T>, IList, IList { /// /// Initializes a new instance of the MtObservableList class that is empty and has the default initial capacity. @@ -56,7 +57,8 @@ namespace Torch.Collections Backing.Insert(index, item); MarkSnapshotsDirty(); OnPropertyChanged(nameof(Count)); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); + OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); } } @@ -69,7 +71,8 @@ namespace Torch.Collections Backing.RemoveAt(index); MarkSnapshotsDirty(); OnPropertyChanged(nameof(Count)); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, old, index)); + OnCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, old, index)); } } @@ -88,7 +91,8 @@ namespace Torch.Collections T old = Backing[index]; Backing[index] = value; MarkSnapshotsDirty(); - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, old, index)); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, + value, old, index)); } } } @@ -121,5 +125,51 @@ namespace Torch.Collections } } } + + /// + int IList.Add(object value) + { + if (value is T t) + using (Lock.WriteUsing()) + { + int index = Backing.Count; + Backing.Add(t); + return index; + } + return -1; + } + + bool IList.Contains(object value) + { + return value is T t && Contains(t); + } + + int IList.IndexOf(object value) + { + return value is T t ? IndexOf(t) : -1; + } + + /// + void IList.Insert(int index, object value) + { + Insert(index, (T) value); + } + + /// + void IList.Remove(object value) + { + if (value is T t) + base.Remove(t); + } + + /// + object IList.this[int index] + { + get => this[index]; + set => this[index] = (T) value; + } + + /// + bool IList.IsFixedSize => false; } -} +} \ No newline at end of file