From 428229e9b0e4c17b26083ef0442666be312bf800 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 1 Nov 2016 23:34:27 -0500 Subject: [PATCH] Optimized the "zero wait time" path in the scheduler and updated the code to always use a scheduler, either the ImmediateScheduler (same as the old default behavior) or the registered scheduler. --- src/Avalonia.Base/AvaloniaObject.cs | 12 ++++++----- .../Threading/AvaloniaScheduler.cs | 20 ++++++++++++++++++- .../AvaloniaObjectTests_Binding.cs | 10 ---------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 5609fde7e9..72ff5187fa 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -305,10 +305,12 @@ namespace Avalonia VerifyAccess(); - var scheduler = AvaloniaLocator.Current.GetService(); - if (scheduler != null) + var description = GetDescription(source); + + if (priority == BindingPriority.LocalValue) { - source = source.ObserveOn(scheduler); + var scheduler = AvaloniaLocator.Current.GetService() ?? ImmediateScheduler.Instance; + source = source.ObserveOn(scheduler); } if (property.IsDirect) @@ -323,7 +325,7 @@ namespace Avalonia this, "Bound {Property} to {Binding} with priority LocalValue", property, - GetDescription(source)); + description); IDisposable subscription = null; @@ -365,7 +367,7 @@ namespace Avalonia this, "Bound {Property} to {Binding} with priority {Priority}", property, - GetDescription(source), + description, priority); return v.Add(source, (int)priority); diff --git a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs index 43815b4ebc..d9361fe7f0 100644 --- a/src/Avalonia.Base/Threading/AvaloniaScheduler.cs +++ b/src/Avalonia.Base/Threading/AvaloniaScheduler.cs @@ -3,6 +3,7 @@ using System; using System.Reactive.Concurrency; +using System.Reactive.Disposables; namespace Avalonia.Threading { @@ -26,7 +27,24 @@ namespace Avalonia.Threading /// public override IDisposable Schedule(TState state, TimeSpan dueTime, Func action) { - return DispatcherTimer.RunOnce(() => action(this, state), dueTime); + var composite = new CompositeDisposable(2); + if (dueTime == TimeSpan.Zero) + { + var cancellation = new CancellationDisposable(); + Dispatcher.UIThread.InvokeAsync(() => + { + if (!cancellation.Token.IsCancellationRequested) + { + composite.Add(action(this, state)); + } + }, DispatcherPriority.DataBind); + composite.Add(cancellation); + } + else + { + composite.Add(DispatcherTimer.RunOnce(() => composite.Add(action(this, state)), dueTime)); + } + return composite; } } } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index c72432029a..5e286305d2 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -370,18 +370,10 @@ namespace Avalonia.Base.UnitTests var target = new Class1(); var source = new Subject(); var currentThreadId = Thread.CurrentThread.ManagedThreadId; - var calledThreadingInterface = false; var threadingInterfaceMock = new Mock(); threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread) .Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId); - threadingInterfaceMock.Setup(mock => mock.StartTimer(TimeSpan.Zero, It.IsAny())) - .Returns((ts, act) => - { - act(); - calledThreadingInterface = true; - return Disposable.Empty; - }); using (AvaloniaLocator.EnterScope()) { @@ -391,8 +383,6 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.QuxProperty, source); await Task.Run(() => source.OnNext(6.7)); - - Assert.True(calledThreadingInterface); } }