Trace level messages

Collection change events are all deferred, and replaced with reset if it makes sense.
This commit is contained in:
Westin Miller
2017-11-09 09:42:48 -08:00
parent 6e3b7e7a04
commit 11bc7cb60c
3 changed files with 163 additions and 48 deletions

View File

@@ -77,7 +77,7 @@ namespace Torch.Server.Managers
if (evm is T m) if (evm is T m)
{ {
var result = _factory(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 result;
} }
return null; return null;
@@ -224,7 +224,7 @@ namespace Torch.Server.Managers
if (factory.Method.GetParameters()[0].ParameterType.IsInstanceOfType(model) && if (factory.Method.GetParameters()[0].ParameterType.IsInstanceOfType(model) &&
factory.DynamicInvoke(model) is Control result) factory.DynamicInvoke(model) is Control result)
{ {
_log.Debug($"Control factory {factory.Method} created {result}"); _log.Trace($"Control factory {factory.Method} created {result}");
return result; return result;
} }
_log.Warn($"No control created for {model}"); _log.Warn($"No control created for {model}");

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
@@ -16,7 +17,8 @@ namespace Torch.Collections
/// </summary> /// </summary>
/// <typeparam name="TC">Collection type</typeparam> /// <typeparam name="TC">Collection type</typeparam>
/// <typeparam name="TV">Value type</typeparam> /// <typeparam name="TV">Value type</typeparam>
public abstract class MtObservableCollection<TC, TV> : INotifyPropertyChanged, INotifyCollectionChanged, IEnumerable<TV> where TC : class, ICollection<TV> public abstract class MtObservableCollection<TC, TV> : INotifyPropertyChanged, INotifyCollectionChanged,
IEnumerable<TV>, ICollection where TC : class, ICollection<TV>
{ {
protected readonly ReaderWriterLockSlim Lock; protected readonly ReaderWriterLockSlim Lock;
protected readonly TC Backing; protected readonly TC Backing;
@@ -30,6 +32,13 @@ namespace Torch.Collections
Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
_version = 0; _version = 0;
_threadViews = new ThreadLocal<ThreadView>(() => new ThreadView(this)); _threadViews = new ThreadLocal<ThreadView>(() => new ThreadView(this));
_deferredSnapshot = new DeferredUpdateToken(this);
_flushEventQueue = new Timer(FlushCollectionEventQueue);
}
~MtObservableCollection()
{
_flushEventQueue.Dispose();
} }
/// <summary> /// <summary>
@@ -53,6 +62,7 @@ namespace Torch.Collections
} }
#region ICollection #region ICollection
/// <inheritdoc/> /// <inheritdoc/>
public void Add(TV item) public void Add(TV item)
{ {
@@ -61,7 +71,8 @@ namespace Torch.Collections
Backing.Add(item); Backing.Add(item);
MarkSnapshotsDirty(); MarkSnapshotsDirty();
OnPropertyChanged(nameof(Count)); 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)); OnPropertyChanged(nameof(Count));
OnCollectionChanged(oldIndex.HasValue OnCollectionChanged(oldIndex.HasValue
? new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, oldIndex.Value) ? new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item,
oldIndex.Value)
: new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); : new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
return true; return true;
} }
@@ -126,11 +138,13 @@ namespace Torch.Collections
/// <inheritdoc/> /// <inheritdoc/>
public bool IsReadOnly => Backing.IsReadOnly; public bool IsReadOnly => Backing.IsReadOnly;
#endregion #endregion
#region Event Wrappers #region Event Wrappers
private readonly WeakReference<DeferredUpdateToken> _deferredSnapshot = new WeakReference<DeferredUpdateToken>(null);
private bool _deferredSnapshotTaken = false; private readonly DeferredUpdateToken _deferredSnapshot;
/// <summary> /// <summary>
/// Disposable that stops update signals and signals a full refresh when disposed. /// Disposable that stops update signals and signals a full refresh when disposed.
/// </summary> /// </summary>
@@ -138,13 +152,8 @@ namespace Torch.Collections
{ {
using (Lock.WriteUsing()) using (Lock.WriteUsing())
{ {
if (_deferredSnapshotTaken) _deferredSnapshot.Enter();
return new DummyToken(); return _deferredSnapshot;
DeferredUpdateToken token;
if (!_deferredSnapshot.TryGetTarget(out token))
_deferredSnapshot.SetTarget(token = new DeferredUpdateToken());
token.SetCollection(this);
return token;
} }
} }
@@ -157,53 +166,81 @@ namespace Torch.Collections
private class DeferredUpdateToken : IDisposable private class DeferredUpdateToken : IDisposable
{ {
private MtObservableCollection<TC, TV> _collection; private readonly MtObservableCollection<TC, TV> _collection;
private int _depth;
internal void SetCollection(MtObservableCollection<TC, TV> c) internal DeferredUpdateToken(MtObservableCollection<TC, TV> c)
{ {
c._deferredSnapshotTaken = true;
_collection = c; _collection = c;
c.NotificationsEnabled = false; }
internal void Enter()
{
if (Interlocked.Increment(ref _depth) == 1)
{
_collection.NotificationsEnabled = false;
}
} }
public void Dispose() public void Dispose()
{ {
using (_collection.Lock.WriteUsing()) if (Interlocked.Decrement(ref _depth) == 0)
{ using (_collection.Lock.WriteUsing())
_collection.NotificationsEnabled = true; {
_collection.OnPropertyChanged(nameof(Count)); _collection.NotificationsEnabled = true;
_collection.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); _collection.OnPropertyChanged(nameof(Count));
_collection._deferredSnapshotTaken = false; _collection.OnCollectionChanged(
} new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
} }
} }
protected void OnPropertyChanged(string propName) protected void OnPropertyChanged(string propName)
{ {
NotifyEvent(this, new PropertyChangedEventArgs(propName)); if (!NotificationsEnabled)
return;
_propertyChangedEvent.Raise(this, new PropertyChangedEventArgs(propName));
} }
protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e) protected void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{ {
NotifyEvent(this, e); if (!NotificationsEnabled)
OnPropertyChanged("Item[]"); 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<NotifyCollectionChangedEventArgs> _collectionEventQueue =
new Queue<NotifyCollectionChangedEventArgs>();
private void FlushCollectionEventQueue(object data)
{ {
if (NotificationsEnabled) bool reset = _collectionEventQueue.Count >= 2;
_propertyChangedEvent.Raise(sender, args); 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) private readonly MtObservableEvent<PropertyChangedEventArgs, PropertyChangedEventHandler> _propertyChangedEvent
{ =
if (NotificationsEnabled)
_collectionChangedEvent.Raise(sender, args);
}
private readonly MtObservableEvent<PropertyChangedEventArgs, PropertyChangedEventHandler> _propertyChangedEvent =
new MtObservableEvent<PropertyChangedEventArgs, PropertyChangedEventHandler>(); new MtObservableEvent<PropertyChangedEventArgs, PropertyChangedEventHandler>();
/// <inheritdoc/> /// <inheritdoc/>
public event PropertyChangedEventHandler PropertyChanged public event PropertyChangedEventHandler PropertyChanged
{ {
@@ -219,8 +256,10 @@ namespace Torch.Collections
} }
} }
private readonly MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler> _collectionChangedEvent = private readonly MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler>
new MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler>(); _collectionChangedEvent =
new MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler>();
/// <inheritdoc/> /// <inheritdoc/>
public event NotifyCollectionChangedEventHandler CollectionChanged public event NotifyCollectionChangedEventHandler CollectionChanged
{ {
@@ -235,6 +274,7 @@ namespace Torch.Collections
OnPropertyChanged(nameof(IsObserved)); OnPropertyChanged(nameof(IsObserved));
} }
} }
#endregion #endregion
/// <summary> /// <summary>
@@ -243,6 +283,7 @@ namespace Torch.Collections
public bool IsObserved => _collectionChangedEvent.IsObserved || _propertyChangedEvent.IsObserved; public bool IsObserved => _collectionChangedEvent.IsObserved || _propertyChangedEvent.IsObserved;
#region Enumeration #region Enumeration
/// <summary> /// <summary>
/// Manages a snapshot to a collection and dispatches enumerators from that snapshot. /// Manages a snapshot to a collection and dispatches enumerators from that snapshot.
/// </summary> /// </summary>
@@ -250,10 +291,12 @@ namespace Torch.Collections
{ {
private readonly MtObservableCollection<TC, TV> _owner; private readonly MtObservableCollection<TC, TV> _owner;
private readonly WeakReference<List<TV>> _snapshot; private readonly WeakReference<List<TV>> _snapshot;
/// <summary> /// <summary>
/// The <see cref="MtObservableCollection{TC,TV}._version"/> of the <see cref="_snapshot"/> /// The <see cref="MtObservableCollection{TC,TV}._version"/> of the <see cref="_snapshot"/>
/// </summary> /// </summary>
private int _snapshotVersion; private int _snapshotVersion;
/// <summary> /// <summary>
/// Number of strong references to the value pointed to be <see cref="_snapshot"/> /// Number of strong references to the value pointed to be <see cref="_snapshot"/>
/// </summary> /// </summary>
@@ -358,6 +401,28 @@ namespace Torch.Collections
/// <inheritdoc/> /// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#endregion #endregion
/// <inheritdoc/>
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++);
}
}
}
/// <inheritdoc/>
object ICollection.SyncRoot => this;
/// <inheritdoc/>
bool ICollection.IsSynchronized => true;
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
@@ -12,7 +13,7 @@ namespace Torch.Collections
/// Multithread safe, observable list /// Multithread safe, observable list
/// </summary> /// </summary>
/// <typeparam name="T">Value type</typeparam> /// <typeparam name="T">Value type</typeparam>
public class MtObservableList<T> : MtObservableCollection<IList<T>, T>, IList<T> public class MtObservableList<T> : MtObservableCollection<IList<T>, T>, IList<T>, IList
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the MtObservableList class that is empty and has the default initial capacity. /// 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); Backing.Insert(index, item);
MarkSnapshotsDirty(); MarkSnapshotsDirty();
OnPropertyChanged(nameof(Count)); 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); Backing.RemoveAt(index);
MarkSnapshotsDirty(); MarkSnapshotsDirty();
OnPropertyChanged(nameof(Count)); 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]; T old = Backing[index];
Backing[index] = value; Backing[index] = value;
MarkSnapshotsDirty(); MarkSnapshotsDirty();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, old, index)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,
value, old, index));
} }
} }
} }
@@ -121,5 +125,51 @@ namespace Torch.Collections
} }
} }
} }
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
void IList.Insert(int index, object value)
{
Insert(index, (T) value);
}
/// <inheritdoc/>
void IList.Remove(object value)
{
if (value is T t)
base.Remove(t);
}
/// <inheritdoc/>
object IList.this[int index]
{
get => this[index];
set => this[index] = (T) value;
}
/// <inheritdoc/>
bool IList.IsFixedSize => false;
} }
} }