+2
Under review

Thread safety and RenderPoints

Nick Donais 5 years ago • updated by Joshua 2 months ago 6
So I've seen the error a few times, mainly when the plot is trying to update and data is being added to the series:

> System.InvalidOperationException occurred
> HResult=-2146233079
> Message=Collection was modified; enumeration operation may not execute.
> Source=mscorlib
> StackTrace:
> at System.Collections.Generic.List`1.Enumerator.MoveNextRare()

Seems to be because ICollection is being used here:
https://github.com/oxyplot/oxyplot/blob/develop/So...
Would it be better to use one of the concurrent collects for this?
Under review
Have you tried using the  `SyncRoot` property on the `PlotModel` to synchronize the updates? Bound collections should not be changed while the plot is updating, I think.
I've experienced this issue a quite a few times during testing, but only if I was adding data and refreshing the chart too fast (under 500ms). It seems to be coming back now in the control I put together that has a custom legend that allows the user to show/hide series easily, and if the plot is being updated at the same time the user clicks show/hide it's shown the error on the plot model for under a second, or even crashed the application.

The main reason I brought up using Concurrent Collections over normal collections is because I'm using OxyPlot currently for real time data, so it's always possible the chart will be updated (from either the set interval or any of the calls that update it, like show or hiding a series, in this event for example) as data is being added into the series on it.

I didn't even know this SyncRoot property existed, I'll look at it more and let you know, Thanks!
Did SyncRoot solve this issue for you?  I'm running into the same issue using real time data.
Yes and no, I still have to limit adding new data to around every 500ms, any faster and I start to see flickering and the error pop up. Honestly I think Concurrent Collections would be a better fix that a lock object for the plot data. Sadly I don't have extra time right now to try and play with the source code >_<.

Just some remarks on this


I was running into this error  this with Xamarin.Android OxyPlot 1.0

The plot is being redrawn / invalidated at 40Hz. This is working fine with SyncRoot. 

But I discovered that when I tap on the plot, I get the "Collection was modified; enumeration operation may not execute" exception. Interesting because I had disabled all touch interactions but looks like OxyPlot still try and process the touch and do something with the collection

foreach (var a in plotView.Model.Axes)
{
    a.IsPanEnabled = false;
    a.IsZoomEnabled = false;
    a.Selectable = false;
}

plotView.FocusableInTouchMode = false;
plotView.Focusable = false;
plotView.FocusedByDefault = false;
plotView.LongClickable = false;
plotView.Clickable = false;

The problematic code that was causing the exception was

if (bubbleSeries.Points.Count == 0)
{
    bubbleSeries.Points.Add(new ScatterPoint(xAxisValue, yAxisValue));
}
else
{
    bubbleSeries.Points[0] = new ScatterPoint(xAxisValue, yAxisValue);
}

model.Background = isWithinAccelTolerence ? OxyColors.LightGreen : OxyColors.LightSalmon;

appActivity.RunOnUiThread(() =>
{
    lock (model.SyncRoot)
    {
        model.InvalidatePlot(false);
    }
});

Fixed by moving points collection to inside SyncRoot area. 

appActivity.RunOnUiThread(() =>
{
lock (model.SyncRoot)
{
if (bubbleSeries.Points.Count == 0)
{
bubbleSeries.Points.Add(new ScatterPoint(xAxisValue, yAxisValue));
}
else
{
bubbleSeries.Points[0] = new ScatterPoint(xAxisValue, yAxisValue);
}

model.Background = isWithinAccelTolerence ? OxyColors.LightGreen : OxyColors.LightSalmon;

model.InvalidatePlot(false);
}
});

My guess is that the touch interaction on the plot view somehow interacted with the Points collection in the background, and of course updating the plot at 40Hz made this problem pretty obvious.

Ah. I missed setting selectable to false for series.

Doing this 

foreach (var s in plotView.Model.Series)
{
s.Selectable = false;
}

Also resolved the issue.