I have an interesting problem with deadlocks in my application. There is an in-memory data store that uses ReaderWriterLockSlim to synchronize read and write. One of the reading methods uses Parallel.ForEach to search the store based on a set of filters. Perhaps one of the filters requires constant reading of the same storage. Here is the deadlock scenario:
UPDATE: Sample code below. Steps Updated with Real Method Calls
Given a Single Instance storeofConcreteStoreThatExtendsGenericStore
- Thread1 gets a read lock in the repository -
store.Search(someCriteria) - Thread2 is trying to update the storage with a write lock
store.Update()-, locks Thread1 - Thread1 runs Parallel.ForEach against the repository to run a set of filters.
- Thread3 (spawned by Thread1 Parallel.ForEach) is trying to read constant time into storage. It tries to get a read lock, but is locked behind a Thread2 write lock .
- Thread1 cannot complete because it cannot join Thread3 . Thread2 cannot complete because it is blocked by Thread1 .
Ideally, what I would like to do is not try to get a read lock if the predecessor thread of the current thread already has the same lock. Is there any way to do this? Or is there a different approach?
public abstract class GenericStore<TKey, TValue>
{
private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private List<IFilter> _filters;
protected Dictionary<TKey, TValue> Store { get; private set; }
public void Update()
{
_lock.EnterWriterLock();
_lock.ExitWriteLock();
}
public TValue GetByKey(TKey key)
{
TValue value;
_lock.EnterReadLock();
value = Store[key];
_lock.ExitReadLock();
return value;
}
public List<TValue> Search(Criteria criteria)
{
List<TValue> matches = new List<TValue>();
_lock.EnterReadLock();
Parallel.ForEach(Store.Values, item =>
{
bool isMatch = true;
foreach(IFilter filter in _filters)
{
if (!filter.Check(criteria, item))
{
isMatch = false;
break;
}
}
if (isMatch)
{
lock(matches)
{
matches.Add(item);
}
}
});
_lock.ExitReadLock();
return matches;
}
}
public class ExampleOffendingFilter : IFilter
{
private ConcreteStoreThatExtendsGenericStore _sameStore;
public bool Check(Criteria criteria, ConcreteValueType item)
{
_sameStore.GetByKey(item.SomeRelatedProperty);
return trueOrFalse;
}
}