Skip to content

Commit a9d7854

Browse files
authored
Fix off-thread InvalidatePlot calls (#47) (#48)
1 parent e858e12 commit a9d7854

File tree

1 file changed

+67
-31
lines changed

1 file changed

+67
-31
lines changed

Source/OxyPlot.Avalonia/PlotBase.cs

Lines changed: 67 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ public abstract partial class PlotBase : TemplatedControl, IPlotView
5757
/// </summary>
5858
private Panel panel;
5959

60+
/// <summary>
61+
/// Invalidation flag (0: no update, 1: update, 2: update date).
62+
/// </summary>
63+
private int isUpdateRequired;
64+
6065
/// <summary>
6166
/// Invalidation flag (0: no update, 1: update visual elements).
6267
/// </summary>
@@ -224,21 +229,22 @@ public void ResetAllAxes()
224229
/// <param name="updateData">The update Data.</param>
225230
public void InvalidatePlot(bool updateData = true)
226231
{
227-
if (Width <= 0 || Height <= 0)
228-
{
229-
return;
230-
}
232+
// perform update on UI thread
233+
var updateState = updateData ? 2 : 1;
234+
int currentState = isUpdateRequired;
231235

232-
// TODO: legend on/off crash (issue with Legend hit-test implementation, really)
233-
UpdateModel(updateData);
234-
235-
if (Interlocked.CompareExchange(ref isPlotInvalidated, 1, 0) == 0)
236+
while (currentState < updateState)
236237
{
237-
// Invalidate the arrange state for the element.
238-
// After the invalidation, the element will have its layout updated,
239-
// which will occur asynchronously unless subsequently forced by UpdateLayout.
240-
BeginInvoke(InvalidateArrange);
241-
BeginInvoke(InvalidateVisual);
238+
if (Interlocked.CompareExchange(ref isUpdateRequired, updateState, currentState) == currentState)
239+
{
240+
isUpdateRequired = updateState;
241+
BeginInvoke(() => UpdateModel(updateData));
242+
break;
243+
}
244+
else
245+
{
246+
currentState = isUpdateRequired;
247+
}
242248
}
243249
}
244250

@@ -386,10 +392,30 @@ protected override Size ArrangeOverride(Size finalSize)
386392
/// <param name="updateData">The update Data.</param>
387393
protected void UpdateModel(bool updateData = true)
388394
{
389-
if (ActualModel != null)
395+
if (Width <= 0 || Height <= 0 || ActualModel == null)
396+
{
397+
return;
398+
}
399+
400+
lock (this.ActualModel.SyncRoot)
390401
{
391-
((IPlotModel)ActualModel).Update(updateData);
402+
var updateState = (Interlocked.Exchange(ref isUpdateRequired, 0));
403+
404+
if (updateState > 0)
405+
{
406+
((IPlotModel)ActualModel).Update(updateState == 2);
407+
}
408+
}
409+
410+
if (Interlocked.CompareExchange(ref isPlotInvalidated, 1, 0) == 0)
411+
{
412+
// Invalidate the arrange state for the element.
413+
// After the invalidation, the element will have its layout updated,
414+
// which will occur asynchronously unless subsequently forced by UpdateLayout.
415+
BeginInvoke(InvalidateArrange);
416+
BeginInvoke(InvalidateVisual);
392417
}
418+
393419
}
394420

395421
/// <summary>
@@ -482,26 +508,36 @@ private void UpdateVisuals()
482508

483509
if (ActualModel != null)
484510
{
485-
if (DisconnectCanvasWhileUpdating)
511+
lock (this.ActualModel.SyncRoot)
486512
{
487-
// TODO: profile... not sure if this makes any difference
488-
var idx = panel.Children.IndexOf(canvas);
489-
if (idx != -1)
513+
var updateState = (Interlocked.Exchange(ref isUpdateRequired, 0));
514+
515+
if (updateState > 0)
490516
{
491-
panel.Children.RemoveAt(idx);
517+
((IPlotModel)ActualModel).Update(updateState == 2);
492518
}
493519

494-
((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height));
495-
496-
// reinsert the canvas again
497-
if (idx != -1)
520+
if (DisconnectCanvasWhileUpdating)
498521
{
499-
panel.Children.Insert(idx, canvas);
522+
// TODO: profile... not sure if this makes any difference
523+
var idx = panel.Children.IndexOf(canvas);
524+
if (idx != -1)
525+
{
526+
panel.Children.RemoveAt(idx);
527+
}
528+
529+
((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height));
530+
531+
// reinsert the canvas again
532+
if (idx != -1)
533+
{
534+
panel.Children.Insert(idx, canvas);
535+
}
536+
}
537+
else
538+
{
539+
((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height));
500540
}
501-
}
502-
else
503-
{
504-
((IPlotModel)ActualModel).Render(renderContext, new OxyRect(0, 0, canvas.Bounds.Width, canvas.Bounds.Height));
505541
}
506542
}
507543
}
@@ -514,11 +550,11 @@ private static void BeginInvoke(Action action)
514550
{
515551
if (Dispatcher.UIThread.CheckAccess())
516552
{
517-
Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Loaded);
553+
action?.Invoke();
518554
}
519555
else
520556
{
521-
action?.Invoke();
557+
Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Loaded);
522558
}
523559
}
524560
}

0 commit comments

Comments
 (0)