From 376ee843caf68bc986627b75b175b4a57e6fbdd5 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 1 Aug 2020 13:55:15 +0300 Subject: [PATCH 1/4] Fixed concurrency issues in RenderLoop --- src/Avalonia.Visuals/Rendering/RenderLoop.cs | 40 ++++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs index 789d028a3a..26011e4b92 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLoop.cs @@ -18,6 +18,7 @@ namespace Avalonia.Rendering { private readonly IDispatcher _dispatcher; private List _items = new List(); + private List _itemsCopy = new List(); private IRenderTimer _timer; private int _inTick; private int _inUpdate; @@ -63,11 +64,14 @@ namespace Avalonia.Rendering Contract.Requires(i != null); Dispatcher.UIThread.VerifyAccess(); - _items.Add(i); - - if (_items.Count == 1) + lock (_items) { - Timer.Tick += TimerTick; + _items.Add(i); + + if (_items.Count == 1) + { + Timer.Tick += TimerTick; + } } } @@ -76,15 +80,17 @@ namespace Avalonia.Rendering { Contract.Requires(i != null); Dispatcher.UIThread.VerifyAccess(); - - _items.Remove(i); - - if (_items.Count == 0) + lock (_items) { - Timer.Tick -= TimerTick; + _items.Remove(i); + + if (_items.Count == 0) + { + Timer.Tick -= TimerTick; + } } } - + private void TimerTick(TimeSpan time) { if (Interlocked.CompareExchange(ref _inTick, 1, 0) == 0) @@ -129,10 +135,20 @@ namespace Avalonia.Rendering }, DispatcherPriority.Render); } - for(int i = 0; i < _items.Count; i++) + lock (_items) { - _items[i].Render(); + _itemsCopy.Clear(); + foreach (var i in _items) + _itemsCopy.Add(i); } + + for (int i = 0; i < _itemsCopy.Count; i++) + { + _itemsCopy[i].Render(); + } + + _itemsCopy.Clear(); + } catch (Exception ex) { From 475fea4d1d2ae82003cc20e13902081ce8560fd9 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 1 Aug 2020 14:04:08 +0300 Subject: [PATCH 2/4] Added SleepLoopRenderTimer because of libnvidia-glcore.so crashes --- .../Rendering/SleepLoopRenderTimer.cs | 33 +++++++++++++++++++ src/Avalonia.X11/X11Platform.cs | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs diff --git a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs new file mode 100644 index 0000000000..a44891a888 --- /dev/null +++ b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs @@ -0,0 +1,33 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace Avalonia.Rendering +{ + public class SleepLoopRenderTimer : IRenderTimer + { + public event Action Tick; + + public SleepLoopRenderTimer(int fps) + { + var timeBetweenTicks = TimeSpan.FromSeconds(1d / fps); + new Thread(() => + { + var st = Stopwatch.StartNew(); + var now = st.Elapsed; + var lastTick = now; + + while (true) + { + var timeTillNextTick = lastTick + timeBetweenTicks - now; + if (timeTillNextTick.TotalMilliseconds > 1) + Thread.Sleep(timeTillNextTick); + + + Tick?.Invoke(now); + now = st.Elapsed; + } + }) { IsBackground = true }.Start(); + } + } +} diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 9e9c4fd30e..7f3255d4da 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -47,7 +47,7 @@ namespace Avalonia.X11 AvaloniaLocator.CurrentMutable.BindToSelf(this) .Bind().ToConstant(this) .Bind().ToConstant(new X11PlatformThreading(this)) - .Bind().ToConstant(new DefaultRenderTimer(60)) + .Bind().ToConstant(new SleepLoopRenderTimer(60)) .Bind().ToConstant(new RenderLoop()) .Bind().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Control)) .Bind().ToFunc(() => KeyboardDevice) From 90aad3b785f4854e768922491bd7ad849fa82a18 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 1 Aug 2020 14:25:50 +0300 Subject: [PATCH 3/4] Stop the loop when there are no subscribers --- .../Rendering/SleepLoopRenderTimer.cs | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs index a44891a888..79e029ccd2 100644 --- a/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs +++ b/src/Avalonia.Visuals/Rendering/SleepLoopRenderTimer.cs @@ -6,28 +6,67 @@ namespace Avalonia.Rendering { public class SleepLoopRenderTimer : IRenderTimer { - public event Action Tick; + private Action _tick; + private int _count; + private readonly object _lock = new object(); + private bool _running; + private readonly Stopwatch _st = Stopwatch.StartNew(); + private readonly TimeSpan _timeBetweenTicks; public SleepLoopRenderTimer(int fps) { - var timeBetweenTicks = TimeSpan.FromSeconds(1d / fps); - new Thread(() => + _timeBetweenTicks = TimeSpan.FromSeconds(1d / fps); + } + + public event Action Tick + { + add { - var st = Stopwatch.StartNew(); - var now = st.Elapsed; - var lastTick = now; + lock (_lock) + { + _tick += value; + _count++; + if (_running) + return; + _running = true; + new Thread(LoopProc) { IsBackground = true }.Start(); + } - while (true) + } + remove + { + lock (_lock) { - var timeTillNextTick = lastTick + timeBetweenTicks - now; - if (timeTillNextTick.TotalMilliseconds > 1) - Thread.Sleep(timeTillNextTick); + _tick -= value; + _count--; + } + } + } + void LoopProc() + { + var now = _st.Elapsed; + var lastTick = now; + + while (true) + { + var timeTillNextTick = lastTick + _timeBetweenTicks - now; + if (timeTillNextTick.TotalMilliseconds > 1) Thread.Sleep(timeTillNextTick); - Tick?.Invoke(now); - now = st.Elapsed; + lock (_lock) + { + if (_count == 0) + { + _running = false; + return; + } } - }) { IsBackground = true }.Start(); + + _tick?.Invoke(now); + now = _st.Elapsed; + } } + + } } From 52d961deed3bff55c46f2d670f26a4ba87068bd2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Sat, 1 Aug 2020 11:42:04 -0300 Subject: [PATCH 4/4] remove whitespace. --- src/Avalonia.Visuals/Rendering/RenderLoop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs index 26011e4b92..b2a6b0da19 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLoop.cs @@ -90,7 +90,7 @@ namespace Avalonia.Rendering } } } - + private void TimerTick(TimeSpan time) { if (Interlocked.CompareExchange(ref _inTick, 1, 0) == 0)