Browse Source

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.

pull/790/head
Jeremy Koritzinsky 10 years ago
parent
commit
428229e9b0
  1. 12
      src/Avalonia.Base/AvaloniaObject.cs
  2. 20
      src/Avalonia.Base/Threading/AvaloniaScheduler.cs
  3. 10
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

12
src/Avalonia.Base/AvaloniaObject.cs

@ -305,10 +305,12 @@ namespace Avalonia
VerifyAccess();
var scheduler = AvaloniaLocator.Current.GetService<IScheduler>();
if (scheduler != null)
var description = GetDescription(source);
if (priority == BindingPriority.LocalValue)
{
source = source.ObserveOn(scheduler);
var scheduler = AvaloniaLocator.Current.GetService<IScheduler>() ?? 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);

20
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
/// <inheritdoc/>
public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> 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;
}
}
}

10
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -370,18 +370,10 @@ namespace Avalonia.Base.UnitTests
var target = new Class1();
var source = new Subject<object>();
var currentThreadId = Thread.CurrentThread.ManagedThreadId;
var calledThreadingInterface = false;
var threadingInterfaceMock = new Mock<IPlatformThreadingInterface>();
threadingInterfaceMock.SetupGet(mock => mock.CurrentThreadIsLoopThread)
.Returns(() => Thread.CurrentThread.ManagedThreadId == currentThreadId);
threadingInterfaceMock.Setup(mock => mock.StartTimer(TimeSpan.Zero, It.IsAny<Action>()))
.Returns<TimeSpan, Action>((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);
}
}

Loading…
Cancel
Save