public IChangeSet <TObject, TKey> Change(IChangeSet <ExpirableItem <TObject, TKey>, TKey> updates) { _cache.Clone(updates); var itemstoexpire = _cache.KeyValues .OrderByDescending(exp => exp.Value.ExpireAt) .Skip(_sizeLimit) .Select(exp => new Change <TObject, TKey>(ChangeReason.Remove, exp.Key, exp.Value.Value)) .ToList(); if (itemstoexpire.Count > 0) { _cache.Remove(itemstoexpire.Select(exp => exp.Key)); } var notifications = _cache.CaptureChanges(); var changed = notifications.Select(update => new Change <TObject, TKey> ( update.Reason, update.Key, update.Current.Value, update.Previous.HasValue ? update.Previous.Value.Value : Optional <TObject> .None )); return(new ChangeSet <TObject, TKey>(changed)); }
/// <summary> /// Initializes a new instance of the <see cref="LockFreeObservableCache{TObject, TKey}"/> class. /// </summary> /// <param name="source">The source.</param> public LockFreeObservableCache(IObservable <IChangeSet <TObject, TKey> > source) { _updater = new CacheUpdater <TObject, TKey>(_innerCache); var loader = source.Select(changes => { _innerCache.Clone(changes); return(_innerCache.CaptureChanges()); }).SubscribeSafe(_changes); _cleanUp = Disposable.Create(() => { loader.Dispose(); _changes.OnCompleted(); _countChanged.OnCompleted(); }); }
private IObservable <IChangeSet <TDestination, TDestinationKey> > CreateWithChangeset() { return(Observable.Create <IChangeSet <TDestination, TDestinationKey> >(observer => { var result = new ChangeAwareCache <TDestination, TDestinationKey>(); var transformed = _source.Transform((t, key) => { //Only skip initial for first time Adds where there is isinitial data records var locker = new object(); var collection = _manyselector(t); var changes = _childChanges(t).Synchronize(locker).Skip(1); return new ManyContainer(() => { lock (locker) return collection.Select(m => new DestinationContainer(m, _keySelector(m))); }, changes); }).Publish(); var outerLock = new object(); var intial = transformed .Synchronize(outerLock) .Select(changes => new ChangeSet <TDestination, TDestinationKey>(new DestinationEnumerator(changes))); var subsequent = transformed .MergeMany(x => x.Changes) .Synchronize(outerLock); var allChanges = intial.Merge(subsequent).Select(changes => { result.Clone(changes); return result.CaptureChanges(); }); return new CompositeDisposable(allChanges.SubscribeSafe(observer), transformed.Connect()); })); }
/// <summary> /// Sorts using the specified sorter. Will return null if there are no changes /// </summary> /// <param name="sortReason">The sort reason.</param> /// <param name="changes">The changes.</param> /// <returns></returns> private ISortedChangeSet <TObject, TKey> DoSort(SortReason sortReason, IChangeSet <TObject, TKey> changes = null) { if (changes != null) { _cache.Clone(changes); changes = _cache.CaptureChanges(); _haveReceivedData = true; if (_comparer == null) { return(null); } } //if the comparer is not set, return nothing if (_comparer == null || !_haveReceivedData) { return(null); } if (!_initialised) { sortReason = SortReason.InitialLoad; _initialised = true; } else if (changes != null && (_resetThreshold > 0 && changes.Count >= _resetThreshold)) { sortReason = SortReason.Reset; } IChangeSet <TObject, TKey> changeSet; switch (sortReason) { case SortReason.InitialLoad: { //For the first batch, changes may have arrived before the comparer was set. //therefore infer the first batch of changes from the cache _calculator = new IndexCalculator <TObject, TKey>(_comparer, _optimisations); changeSet = _calculator.Load(_cache); } break; case SortReason.Reset: { _calculator.Reset(_cache); changeSet = changes; } break; case SortReason.DataChanged: { changeSet = _calculator.Calculate(changes); } break; case SortReason.ComparerChanged: { changeSet = _calculator.ChangeComparer(_comparer); if (_resetThreshold > 0 && _cache.Count >= _resetThreshold) { sortReason = SortReason.Reset; _calculator.Reset(_cache); } else { sortReason = SortReason.Reorder; changeSet = _calculator.Reorder(); } } break; case SortReason.Reorder: { changeSet = _calculator.Reorder(); } break; default: throw new ArgumentOutOfRangeException(nameof(sortReason)); } Debug.Assert(changeSet != null, "changeSet != null"); if ((sortReason == SortReason.InitialLoad || sortReason == SortReason.DataChanged) && changeSet.Count == 0) { return(null); } if (sortReason == SortReason.Reorder && changeSet.Count == 0) { return(null); } _sorted = new KeyValueCollection <TObject, TKey>(_calculator.List, _comparer, sortReason, _optimisations); return(new SortedChangeSet <TObject, TKey>(_sorted, changeSet)); }
public void Update(IChangeSet <TObject, TKey> changes) { _cache.Clone(changes); }
public IObservable <IChangeSet <TObject, TKey> > Run() { return(Observable.Create <IChangeSet <TObject, TKey> > ( observer => { var result = new ChangeAwareCache <TObject, TKey>(); var locker = new object(); var paused = _intialPauseState; var timeoutDisposer = new SerialDisposable(); var intervalTimerDisposer = new SerialDisposable(); void ResumeAction() { //publish changes (if there are any) var changes = result.CaptureChanges(); if (changes.Count > 0) { observer.OnNext(changes); } } IDisposable IntervalFunction() { return _intervalTimer .Synchronize(locker) .Finally(() => paused = false) .Subscribe(_ => { paused = false; ResumeAction(); if (_intervalTimer != null) { paused = true; } }); } if (_intervalTimer != null) { intervalTimerDisposer.Disposable = IntervalFunction(); } var pausedHander = _pauseIfTrueSelector // .StartWith(initalp) .Synchronize(locker) .Subscribe(p => { paused = p; if (!p) { //pause window has closed, so reset timer if (_timeOut.HasValue) { timeoutDisposer.Disposable = Disposable.Empty; } ResumeAction(); } else { if (_timeOut.HasValue) { timeoutDisposer.Disposable = Observable.Timer(_timeOut.Value, _scheduler) .Synchronize(locker) .Subscribe(_ => { paused = false; ResumeAction(); }); } } }); var publisher = _source .Synchronize(locker) .Subscribe(changes => { result.Clone(changes); //publish if not paused if (!paused) { observer.OnNext(result.CaptureChanges()); } }); return new CompositeDisposable(publisher, pausedHander, timeoutDisposer, intervalTimerDisposer); } )); }