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)
{
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}");

View File

@@ -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
/// </summary>
/// <typeparam name="TC">Collection 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 TC Backing;
@@ -30,6 +32,13 @@ namespace Torch.Collections
Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
_version = 0;
_threadViews = new ThreadLocal<ThreadView>(() => new ThreadView(this));
_deferredSnapshot = new DeferredUpdateToken(this);
_flushEventQueue = new Timer(FlushCollectionEventQueue);
}
~MtObservableCollection()
{
_flushEventQueue.Dispose();
}
/// <summary>
@@ -53,6 +62,7 @@ namespace Torch.Collections
}
#region ICollection
/// <inheritdoc/>
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
/// <inheritdoc/>
public bool IsReadOnly => Backing.IsReadOnly;
#endregion
#region Event Wrappers
private readonly WeakReference<DeferredUpdateToken> _deferredSnapshot = new WeakReference<DeferredUpdateToken>(null);
private bool _deferredSnapshotTaken = false;
private readonly DeferredUpdateToken _deferredSnapshot;
/// <summary>
/// Disposable that stops update signals and signals a full refresh when disposed.
/// </summary>
@@ -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<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;
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<NotifyCollectionChangedEventArgs> _collectionEventQueue =
new Queue<NotifyCollectionChangedEventArgs>();
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<PropertyChangedEventArgs, PropertyChangedEventHandler> _propertyChangedEvent =
private readonly MtObservableEvent<PropertyChangedEventArgs, PropertyChangedEventHandler> _propertyChangedEvent
=
new MtObservableEvent<PropertyChangedEventArgs, PropertyChangedEventHandler>();
/// <inheritdoc/>
public event PropertyChangedEventHandler PropertyChanged
{
@@ -219,8 +256,10 @@ namespace Torch.Collections
}
}
private readonly MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler> _collectionChangedEvent =
new MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler>();
private readonly MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler>
_collectionChangedEvent =
new MtObservableEvent<NotifyCollectionChangedEventArgs, NotifyCollectionChangedEventHandler>();
/// <inheritdoc/>
public event NotifyCollectionChangedEventHandler CollectionChanged
{
@@ -235,6 +274,7 @@ namespace Torch.Collections
OnPropertyChanged(nameof(IsObserved));
}
}
#endregion
/// <summary>
@@ -243,6 +283,7 @@ namespace Torch.Collections
public bool IsObserved => _collectionChangedEvent.IsObserved || _propertyChangedEvent.IsObserved;
#region Enumeration
/// <summary>
/// Manages a snapshot to a collection and dispatches enumerators from that snapshot.
/// </summary>
@@ -250,10 +291,12 @@ namespace Torch.Collections
{
private readonly MtObservableCollection<TC, TV> _owner;
private readonly WeakReference<List<TV>> _snapshot;
/// <summary>
/// The <see cref="MtObservableCollection{TC,TV}._version"/> of the <see cref="_snapshot"/>
/// </summary>
private int _snapshotVersion;
/// <summary>
/// Number of strong references to the value pointed to be <see cref="_snapshot"/>
/// </summary>
@@ -358,6 +401,28 @@ namespace Torch.Collections
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
#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.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
@@ -12,7 +13,7 @@ namespace Torch.Collections
/// Multithread safe, observable list
/// </summary>
/// <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>
/// 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
}
}
}
/// <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;
}
}