Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Wednesday, May 20, 2009 10:46 AM
I'm getting the following exception.
Exception Occured: System.InvalidOperationException: Failed to compare two elements in the array. > System.NullReferenceException: Object reference not set to an instance of an object.
at System.Windows.Forms.Layout.TableLayout.PreAssignedPositionComparer.Compare(Object x, Object y)
at System.Array.SorterObjectArray.SwapIfGreaterWithItems(Int32 a, Int32 b)
End of inner exception stack trace
at System.Array.SorterObjectArray.SwapIfGreaterWithItems(Int32 a, Int32 b)
at System.Array.SorterObjectArray.QuickSort(Int32 left, Int32 right)
at System.Array.Sort(Array keys, Array items, Int32 index, Int32 length, IComparer comparer)
at System.Array.Sort(Array array, IComparer comparer)
at System.Windows.Forms.Layout.TableLayout.ContainerInfo.get_FixedChildrenInfo()
at System.Windows.Forms.Layout.TableLayout.xAssignRowsAndColumns(ContainerInfo containerInfo, LayoutInfo[] childrenInfo, Int32 maxColumns, Int32 maxRows, TableLayoutPanelGrowStyle growStyle)
at System.Windows.Forms.Layout.TableLayout.AssignRowsAndColumns(ContainerInfo containerInfo)
at System.Windows.Forms.Layout.TableLayout.EnsureRowAndColumnAssignments(IArrangedElement container, ContainerInfo containerInfo, Boolean doNotCache)
at System.Windows.Forms.Layout.TableLayout.GetControlFromPosition(IArrangedElement container, Int32 column, Int32 row)
at System.Windows.Forms.TableLayoutSettings.GetControlFromPosition(Int32 column, Int32 row)
at System.Windows.Forms.TableLayoutPanel.GetControlFromPosition(Int32 column, Int32 row)
at iPop.StatsControl.FillValues(EventPackageInfo eventPackageInfo)
The FillValues source code is
try
{
string statisticType = eventPackageInfo.Statistics.GetStatistic(0).Metric.StatisticType.ToString();
int packID = eventPackageInfo.PackageId;
tempvalue = eventPackageInfo.Statistics.GetStatistic(0).StringValue;
string format = dispFormat[statisticType].ToString();
if (format.ToLower() == "t")
{
TimeSpan span = TimeSpan.FromSeconds(Convert.ToInt32(tempvalue));
tempvalue = span.ToString();
}
else if (format.ToLower() == "d")
{
tempvalue = Convert.ToDouble(tempvalue).ToString();
}
else if (format.ToLower() == "p")
{
Decimal dec = decimal.Parse(tempvalue, System.Globalization.NumberStyles.Any);
tempvalue = dec.ToString("n");
}
if (globalPackageIDStart != 0)
{
if (packID < globalPackageIDStart)
{
position = (int)agentControlCellPosition[statisticType];
//agentcontrols
tempLabel = (Label)agentPanel.GetControlFromPosition(position, 1);
this.Invoke(new WriteToAgentControlCallback(WriteToAgentPanel));
}
else
{
//globalcontrols
position = (int)globalControlCellPosition[statisticType];
tempLabel = (Label)globalPanel.GetControlFromPosition(position, 1);
//MessageBox.Show(tempLabel.Text);
this.Invoke(new WriteToAgentControlCallback(WriteToGlobalPanel));
}
}
}
catch (ThreadAbortException x)
{
}
catch (Exception ex)
{
logger.Error("Exception Occured: " + ex.ToString());
//MessageBox.Show(ex.ToString());
}
It is an control whose labels are created randomly, and the values are added to the label.Thanks & Regards, Srinivasan
All replies (6)
Thursday, May 21, 2009 11:47 AM âś…Answered | 1 vote
Well, yes and no. Aside from the fact that you must always lock something (your code would not even compile), a lock only works if every thread that uses a shared resource enters the same lock before accessing the resource itself.
So, we should do what you suggested:
lock (myList) {
string [] result = new string [myList.Count];'
...
}
but we must also ensure that the other thread (that I didn't show originally) does the same before changing the collection. For instance:
lock (myList) {
if (myList.Count > 0) {
myList.RemoveAt (0);
}
}
HTH
--mc
Wednesday, May 20, 2009 12:03 PM | 1 vote
That error means that you tried to use the Compare(object objA, object objB) method and one of the objects was not properly initialized. Where in the code is the error thrown?
Have you placed any break points to see what is happening?There are 10 types of people in this world, those who understand Binary, and those who don't.
Wednesday, May 20, 2009 2:03 PM | 1 vote
Srinivasan,
The issue is probably generated by a race condition... or at least that was the only way I could reproduce it. The code you posted seems to be running in a separate thread from the UI; if this is the case and if the UI thread changes the contents of the TableLayoutPanel, it may well happen that the call to TableLayoutPanel.GetControlFromPosition ends up working on an invalid control collection (TableLayoutControlCollection is not thread safe).
The reason why you get that (rather baffling) exception is due to the comparer used internally by the TableLayoutPanel control, which does not handle null values.
There are two possible solutions I can see: one is to use Invoke to make sure that GetControlFromPosition is executed by the UI thread (thus removing any race condition with that thread), the other is to use locking, so that you never get to operate on an invalid control array.
HTH
--mc
Wednesday, May 20, 2009 2:55 PM
This exception is raised very rarely and not always. I'm just calling the FillValues in a different thread because i'm getting the values asynchronously.Thanks & Regards, Srinivasan
Wednesday, May 20, 2009 6:44 PM | 2 votes
Race conditions do not necessarily cause problems consistently; it depends on how large is the area where the race condition can occurr and how frequently that code gets called.
Just to give you an idea, consider the following code:
private List<string> myList;
private string [] GetArray () {
string [] result = new string [myList.Count];
for (int i = 0; i < myList.Count; i++) {
result [i] = myList [i];
}
return result;
}
Now, what happens if we have another thread that adds or removes elements from myList _after_ the array is allocated? If the other thread is adding elements, we get an IndexOutOfRangeException, if the other thread removes elements, we end up with spurious null elements at the end of the array, which may lead to unpredictable consequences. We may get an exception somewhere, if we are lucky, or we may get invalid data down the road (and good luck debugging that).
As I mentioned at the beginning, how often this will cause a problem depends on how often GetArray is called and how often the other thread modifies myList. If this only happens a few times per run, you may never see an exception... until the day you do a demo, of course.
HTH
--mc
Thursday, May 21, 2009 9:15 AM
private List<string> myList;
private string [] GetArray () {
lock
{
string [] result = new string [myList.Count];
for (int i = 0; i < myList.Count; i++) {
result [i] = myList [i];
}
}
return result;}
Whether this solves the problem?
Thanks & Regards, Srinivasan