diff --git a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs index 2e5b2902f4..77dfc60b83 100644 --- a/src/Android/Avalonia.Android/AndroidThreadingInterface.cs +++ b/src/Android/Avalonia.Android/AndroidThreadingInterface.cs @@ -30,7 +30,7 @@ namespace Avalonia.Android return; } - public IDisposable StartTimer(TimeSpan interval, Action tick) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { if (interval.TotalMilliseconds < 10) interval = TimeSpan.FromMilliseconds(10); diff --git a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs index 68f9e2c631..9f5417ca95 100644 --- a/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs +++ b/src/Avalonia.Base/Platform/IPlatformThreadingInterface.cs @@ -17,10 +17,11 @@ namespace Avalonia.Platform /// /// Starts a timer. /// + /// /// The interval. /// The action to call on each tick. /// An used to stop the timer. - IDisposable StartTimer(TimeSpan interval, Action tick); + IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick); void Signal(DispatcherPriority priority); diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs index a60b663bed..a4b1aaafbc 100644 --- a/src/Avalonia.Base/Threading/Dispatcher.cs +++ b/src/Avalonia.Base/Threading/Dispatcher.cs @@ -84,6 +84,19 @@ namespace Avalonia.Threading _jobRunner?.Post(action, priority); } + /// + /// This is needed for platform backends that don't have internal priority system (e. g. win32) + /// To ensure that there are no jobs with higher priority + /// + /// + internal void EnsurePriority(DispatcherPriority currentPriority) + { + if (currentPriority == DispatcherPriority.MaxValue) + return; + currentPriority += 1; + _jobRunner.RunJobs(currentPriority); + } + /// /// Allows unit tests to change the platform threading interface. /// diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs index 972fdb2049..4a8a9d673f 100644 --- a/src/Avalonia.Base/Threading/DispatcherTimer.cs +++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs @@ -17,13 +17,11 @@ namespace Avalonia.Threading private readonly DispatcherPriority _priority; private TimeSpan _interval; - - private readonly Action _raiseTickAction; - + /// /// Initializes a new instance of the class. /// - public DispatcherTimer() : this(DispatcherPriority.Normal) + public DispatcherTimer() : this(DispatcherPriority.Background) { } @@ -34,7 +32,6 @@ namespace Avalonia.Threading public DispatcherTimer(DispatcherPriority priority) { _priority = priority; - _raiseTickAction = RaiseTick; } /// @@ -187,7 +184,7 @@ namespace Avalonia.Threading throw new Exception("Could not start timer: IPlatformThreadingInterface is not registered."); } - _timer = threading.StartTimer(Interval, InternalTick); + _timer = threading.StartTimer(_priority, Interval, InternalTick); } } @@ -210,14 +207,7 @@ namespace Avalonia.Threading /// private void InternalTick() { - Dispatcher.UIThread.InvokeAsync(_raiseTickAction, _priority); - } - - /// - /// Raises the event. - /// - private void RaiseTick() - { + Dispatcher.UIThread.EnsurePriority(_priority); Tick?.Invoke(this, EventArgs.Empty); } } diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 10405037ab..aefd873155 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -70,15 +70,13 @@ namespace Avalonia.Gtk3 Native.GtkMainIteration(); } - public IDisposable StartTimer(TimeSpan interval, Action tick) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { var msec = interval.TotalMilliseconds; - if (msec <= 0) - throw new ArgumentException("Don't know how to create a timer with zero or negative interval"); var imsec = (uint) msec; if (imsec == 0) imsec = 1; - return GlibTimeout.StarTimer(imsec, tick); + return GlibTimeout.StartTimer(GlibPriority.FromDispatcherPriority(priority), imsec, tick); } private bool[] _signaled = new bool[(int) DispatcherPriority.MaxValue + 1]; diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs index 5253be5afb..0ab4ef980c 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs @@ -48,12 +48,10 @@ namespace Avalonia.Gtk3.Interop } } - public static IDisposable StarTimer(uint interval, Action tick) + public static IDisposable StartTimer(int priority, uint interval, Action tick) { - if (interval == 0) - throw new ArgumentException("Don't know how to create a timer with zero or negative interval"); var timer = new Timer (); - GlibTimeout.Add(GlibPriority.FromDispatcherPriority(DispatcherPriority.Normal), interval, + GlibTimeout.Add(priority, interval, () => { if (timer.Stopped) diff --git a/src/Linux/Avalonia.LinuxFramebuffer/PlatformThreadingInterface.cs b/src/Linux/Avalonia.LinuxFramebuffer/PlatformThreadingInterface.cs index 3aef6944af..e6d21fca36 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/PlatformThreadingInterface.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/PlatformThreadingInterface.cs @@ -17,7 +17,7 @@ namespace Avalonia.LinuxFramebuffer public PlatformThreadingInterface() { TlsCurrentThreadIsLoopThread = true; - StartTimer(new TimeSpan(0, 0, 0, 0, 66), () => Tick?.Invoke(this, new EventArgs())); + StartTimer(DispatcherPriority.Render, new TimeSpan(0, 0, 0, 0, 66), () => Tick?.Invoke(this, new EventArgs())); } private readonly AutoResetEvent _signaled = new AutoResetEvent(false); @@ -74,7 +74,7 @@ namespace Avalonia.LinuxFramebuffer } } - public IDisposable StartTimer(TimeSpan interval, Action tick) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { return new WatTimer(new System.Threading.Timer(delegate { diff --git a/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs index 80c854f5a5..184416e77a 100644 --- a/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs +++ b/src/OSX/Avalonia.MonoMac/PlatformThreadingInterface.cs @@ -16,7 +16,7 @@ namespace Avalonia.MonoMac public event Action Signaled; - public IDisposable StartTimer(TimeSpan interval, Action tick) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) => NSTimer.CreateRepeatingScheduledTimer(interval, () => tick()); public void Signal(DispatcherPriority prio) diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs index a260efd9b9..e265749249 100644 --- a/src/Windows/Avalonia.Win32/Win32Platform.cs +++ b/src/Windows/Avalonia.Win32/Win32Platform.cs @@ -114,7 +114,7 @@ namespace Avalonia.Win32 } } - public IDisposable StartTimer(TimeSpan interval, Action callback) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action callback) { UnmanagedMethods.TimerProc timerDelegate = (hWnd, uMsg, nIDEvent, dwTime) => callback(); diff --git a/src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs b/src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs index 6d6a5e22ca..43a620cccd 100644 --- a/src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs +++ b/src/iOS/Avalonia.iOS/PlatformThreadingInterface.cs @@ -51,7 +51,7 @@ namespace Avalonia.iOS } }*/ - public IDisposable StartTimer(TimeSpan interval, Action tick) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) => NSTimer.CreateRepeatingScheduledTimer(interval, _ => tick()); public void Signal(DispatcherPriority prio) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs index 229a34643d..09aedbdf9c 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Threading.cs @@ -160,7 +160,7 @@ namespace Avalonia.Base.UnitTests throw new NotImplementedException(); } - public IDisposable StartTimer(TimeSpan interval, Action tick) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.RenderTests/TestBase.cs b/tests/Avalonia.RenderTests/TestBase.cs index 409870ed0f..84860eefdb 100644 --- a/tests/Avalonia.RenderTests/TestBase.cs +++ b/tests/Avalonia.RenderTests/TestBase.cs @@ -161,7 +161,7 @@ namespace Avalonia.Direct2D1.RenderTests throw new NotImplementedException(); } - public IDisposable StartTimer(TimeSpan interval, Action tick) + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { throw new NotImplementedException(); }