views:

121

answers:

3

I have the following dictionary:

Dictionary<long, ChangeLogProcess> _changeLogProcesses = 
    new Dictionary<long, ChangeLogProcess>();

I have a method that attempts to get the next changelogprocess in the dictionary of a particular status (If there are no items of a particular status it returns null):

 var changeLogProcesses =
     from entry in _changeLogProcesses
     where (entry.Value.Status == status)
     select entry.Value;

 changeLogProcess = changeLogProcesses.FirstOrDefault<ChangeLogProcess>();

However, during execution it is throwing a stack overflow exception during the linq query? I have done numerous tests to make sure that there are items in teh list and so on but the problem persists?

It's worth noting that this method is part of a service that is running in a multi threaded environment. The linq query above (and all access to it, such as items added/removed to the list, or status changes to the items in the list) are all wrapped in ReaderWriterLockSlim write locks. Again, I have debugged it extensively to make sure there is never anymore than a single thread accessing the list at any time.

What might cause it to stack overflow, as apposed to some possible other errors such as a modification of the list during the query? (again I'm there is only a single thread accessing the list at any one time)

EDIT: as requested the getter and setter code:

 public ChangeLogProcessStatus Status 
 {
    get { return _status; }
    set
    {
        //more that one place can initiate a retry now, so retry count is handled in the property setter
        if (PreviousStatus <= ChangeLogProcessStatus.Waiting && value >= ChangeLogProcessStatus.RetryWaiting)
        {
            this.ChangeLog.Tries++;

            //If it's retry waiting, remove this last service machine from the 
            //list so it can try it again because it's not an error
            if (value == ChangeLogProcessStatus.RetryWaiting && _previousServiceMachineIds.Count > 0)
            {   
                _previousServiceMachineIds.RemoveAt(_previousServiceMachineIds.Count() - 1);
            }
        }

        PreviousStatus = _status;
        _status = value;

    }
}      

LAST EDIT - I've removed the previous examples as the problem did not exist in that code.

It turns out it was in a different part of the application, and it was a very hard to find piece of recursion. It was a coincidence that the stack overflow error was raised duringthe linq query, which as a result was being called 420000+ times recursively.

All answers below were helpfull and on the right path to finding the problem in multi-threaded apps, however the first answer definitly emphasized recursion as the problem which is what it turned out to be (although it wasn't one of the property accessors as seemed obvious).

THanks again

Thanks

+4  A: 

Check the property on the ChangeLogProcess class to make sure that it isn't self-referential. This is, I think, the most likely cause of a stack overflow exception in this case.

Ex:

 private ChangeLogStatus status;
 public ChangeLogStatus Status
 {
      get { return this.Status; }  // instead of this.status
      set { this.status = value }
 }

Another possible alternative is in the equality check for status. Have you overridden Equals() for ChangeLogStatus? Check there to make sure you don't have any self-referential code (or at least a way of terminating the recursion).

tvanfosson
Could you elaborate by any chance?
Mike Tours
The getter for the status is as follows: get { return _status; }
Mike Tours
I added an example of one with an error.
tvanfosson
This is probably the most common source of SO's (or method A calling B which calls A).
Will
Do you mean "this.Value" - if so I understand but as far as I can see it's OK in my example? I've posted further properties used in the setter in the example.
Mike Tours
I didn't look closely enough, the Value is the KeyValuePair property and I'm sure it's ok. You might want to look at the definition of Equals for ChangeLogStatus for self-references. That's really the only other place I can think of.
tvanfosson
ah right, I see how entry.Value might look confusing but you're right it's the Value of the KeyvaluePair. I've not overloaded any operators, so, the Equals operator should be reading the simple Getter on the Status as get{return _status;}Thanks for your help ... I'm at a dead end ...
Mike Tours
Thank you very much for your help. I finally worked it out, and While it turns out the issue had nothing to do with any of the above code, the error was always reported during the linq query.All answers above were really helpfull, but I decided to give this one the correct falg because it emphasised the Recursion. I spent the whole evening going through the app adding counters here and there and found a method that was accessed 42000 times when it should have only been accessed 50 times. This lead me to find the recursion which had been added by a simply mistake.Thanks you.
Mike Tours
+2  A: 

I've noticed that some collections behave very badly when touched by two threads at the same time.

You are actually allowing more than one thread to touch the collection at the same time. RWLS allows multiple threads to access during read operations and lock on a write operation. So two threads could be reading, i.e. touching, the collection at the same time.

My suggestion would be to change the RWLS to a simple lock() and try to repro the stack overflow.

If this fixes your issue, I'd suggest thinking about moving to 4.0 to take advantage of the concurrent collections. Alternatively, you might want to construct your own thread-safe collection cough reflector cough so you can control this situation better.

Will
Thanks Will. As mentioned, even during reads such as rthis I'm using a WRITE LOCK of the RWLS. I have also added debug code (inrementing and decrementing a number) to ensure that there is never ever more than a single operation being performed on the dictionary at any one time. Thanks.
Mike Tours
@mike assumptions are horrible things. They get me every time, at least. Can you repro in a single threaded test? Try locking for fun and possible profit.
Will
I can confirm it works in a single threaded example. Do you know what the absolute worst part of this example is, that, If I add a console.Writeline("debugsomething"); just above the linq query .. it works. Which is absurd IMO ....
Mike Tours
+1  A: 

http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

Thread Safety

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

A Dictionary<(Of <(TKey, TValue>)>) can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

Emphasis mine.

David B
I've just added the full method implementation ... The synchronisation is handled by a reader writer lock slim WRITE lock. Is this sufficient?
Mike Tours
the collection must be locked during the entire enumeration.
David B
Thanks David. I have posted the full locking procedure ...
Mike Tours