public static ILiveList<int> Range(ILiveValue<int> startValue, ILiveValue<int> countValue) { LiveObserver<IValueState<int>> startObserver = null; LiveObserver<IValueState<int>> countObserver = null; return LiveListObservable<int>.Create( innerChanged => { countValue.Subscribe(countObserver = countValue.CreateObserver(innerChanged)); startValue.Subscribe(startObserver = startValue.CreateObserver(innerChanged)); return Lockers.Empty; }, (innerChanged, notified, stateLock, oldState) => { // get state var count = countObserver.GetState(); var start = startObserver.GetState(); var newStatus = count.Status.And(start.Status); // work out delta ListDelta<int> delta = null; if (newStatus.IsDeltaRelevant() && (count.HasChange || start.HasChange)) { delta = new ListDelta<int>(); // adjust head var head = Math.Min(Math.Max(start.OldValue - start.NewValue, -count.OldValue), count.NewValue); if (head < 0) delta.Delete(0, Enumerable.Range(start.OldValue, -head)); else if (head > 0) delta.Insert(0, Enumerable.Range(start.NewValue, head)); var currentCount = count.OldValue + head; var leftoverCount = count.OldValue + Math.Min(0, head); // adjust tail var tail = count.NewValue - currentCount; if (tail > 0) delta.Insert(currentCount, Enumerable.Range(start.NewValue + currentCount, tail)); else if (tail < 0) delta.Delete(count.NewValue, Enumerable.Range(start.OldValue + count.OldValue + tail, -tail)); } // work out new state var result = new CollectionState<int, IListDelta<int>, IList<int>>(); result.SetState( oldState.GetStatus().Add(newStatus), delta, Enumerable.Range(start.NewValue, count.NewValue), Math.Max(count.LastUpdated, start.LastUpdated), stateLock); return result; }, () => { startObserver.Dispose(); countObserver.Dispose(); }); }
public static IDisposable Schedule(this IScheduler scheduler, ILiveValue<TimeSpan> interval, Action action) { var startTime = HiResTimer.Now(); var gate = new object(); var hasValue = false; var cancelable = new SerialDisposable(); var id = 0UL; var observer = interval.CreateObserver( o => Publish.OnConsume( () => { // get new interval var state = o.GetState(); // work out interval from here var timeSpan = TimeSpan.Zero; if (state.Status.IsConnected()) { timeSpan = state.NewValue - HiResTimer.ToTimeSpan(HiResTimer.Now() - startTime); if (timeSpan < TimeSpan.Zero) timeSpan = TimeSpan.Zero; } // adapted from Observable.Throttle() ulong currentid; lock (gate) { hasValue = true; id += 1UL; currentid = id; } var timer = new SingleAssignmentDisposable(); cancelable.Disposable = timer; timer.Disposable = scheduler.Schedule( timeSpan, () => { lock (gate) { if (hasValue && (id == currentid)) action(); hasValue = false; } }); }) ); interval.Subscribe(observer); return new CompositeDisposable(new IDisposable[] { observer, cancelable }); }