Tighten up OpenSim.Framework.Cache locking to avoid race conditions.

This is to resolve a reported issue in http://opensimulator.org/mantis/view.php?id=6232
Here, the land management module is using OpenSim.Framework.Cache (the only code to currently do so apart from the non-default CoreAssetCache).
integration
Justin Clark-Casey (justincc) 2012-08-20 20:55:58 +01:00
parent bcbd450fe4
commit 970727e57e
1 changed files with 53 additions and 30 deletions

View File

@ -199,7 +199,14 @@ namespace OpenSim.Framework
// //
public class Cache public class Cache
{ {
/// <summary>
/// Must only be accessed under lock.
/// </summary>
private List<CacheItemBase> m_Index = new List<CacheItemBase>(); private List<CacheItemBase> m_Index = new List<CacheItemBase>();
/// <summary>
/// Must only be accessed under m_Index lock.
/// </summary>
private Dictionary<string, CacheItemBase> m_Lookup = private Dictionary<string, CacheItemBase> m_Lookup =
new Dictionary<string, CacheItemBase>(); new Dictionary<string, CacheItemBase>();
@ -320,19 +327,19 @@ namespace OpenSim.Framework
{ {
if (m_Lookup.ContainsKey(index)) if (m_Lookup.ContainsKey(index))
item = m_Lookup[index]; item = m_Lookup[index];
}
if (item == null) if (item == null)
{ {
Expire(true);
return null;
}
item.hits++;
item.lastUsed = DateTime.Now;
Expire(true); Expire(true);
return null;
} }
item.hits++;
item.lastUsed = DateTime.Now;
Expire(true);
return item; return item;
} }
@ -385,7 +392,10 @@ namespace OpenSim.Framework
// //
public Object Find(Predicate<CacheItemBase> d) public Object Find(Predicate<CacheItemBase> d)
{ {
CacheItemBase item = m_Index.Find(d); CacheItemBase item;
lock (m_Index)
item = m_Index.Find(d);
if (item == null) if (item == null)
return null; return null;
@ -419,12 +429,12 @@ namespace OpenSim.Framework
public virtual void Store(string index, Object data, Type container, public virtual void Store(string index, Object data, Type container,
Object[] parameters) Object[] parameters)
{ {
Expire(false);
CacheItemBase item; CacheItemBase item;
lock (m_Index) lock (m_Index)
{ {
Expire(false);
if (m_Index.Contains(new CacheItemBase(index))) if (m_Index.Contains(new CacheItemBase(index)))
{ {
if ((m_Flags & CacheFlags.AllowUpdate) != 0) if ((m_Flags & CacheFlags.AllowUpdate) != 0)
@ -450,9 +460,17 @@ namespace OpenSim.Framework
m_Index.Add(item); m_Index.Add(item);
m_Lookup[index] = item; m_Lookup[index] = item;
} }
item.Store(data); item.Store(data);
} }
/// <summary>
/// Expire items as appropriate.
/// </summary>
/// <remarks>
/// Callers must lock m_Index.
/// </remarks>
/// <param name='getting'></param>
protected virtual void Expire(bool getting) protected virtual void Expire(bool getting)
{ {
if (getting && (m_Strategy == CacheStrategy.Aggressive)) if (getting && (m_Strategy == CacheStrategy.Aggressive))
@ -475,12 +493,10 @@ namespace OpenSim.Framework
switch (m_Strategy) switch (m_Strategy)
{ {
case CacheStrategy.Aggressive: case CacheStrategy.Aggressive:
if (Count < Size) if (Count < Size)
return; return;
lock (m_Index)
{
m_Index.Sort(new SortLRU()); m_Index.Sort(new SortLRU());
m_Index.Reverse(); m_Index.Reverse();
@ -490,7 +506,7 @@ namespace OpenSim.Framework
ExpireDelegate doExpire = OnExpire; ExpireDelegate doExpire = OnExpire;
if (doExpire != null) if (doExpire != null)
{ {
List<CacheItemBase> candidates = List<CacheItemBase> candidates =
m_Index.GetRange(target, Count - target); m_Index.GetRange(target, Count - target);
@ -513,27 +529,34 @@ namespace OpenSim.Framework
foreach (CacheItemBase item in m_Index) foreach (CacheItemBase item in m_Index)
m_Lookup[item.uuid] = item; m_Lookup[item.uuid] = item;
} }
}
break; break;
default:
break; default:
break;
} }
} }
public void Invalidate(string uuid) public void Invalidate(string uuid)
{ {
if (!m_Lookup.ContainsKey(uuid)) lock (m_Index)
return; {
if (!m_Lookup.ContainsKey(uuid))
return;
CacheItemBase item = m_Lookup[uuid]; CacheItemBase item = m_Lookup[uuid];
m_Lookup.Remove(uuid); m_Lookup.Remove(uuid);
m_Index.Remove(item); m_Index.Remove(item);
}
} }
public void Clear() public void Clear()
{ {
m_Index.Clear(); lock (m_Index)
m_Lookup.Clear(); {
m_Index.Clear();
m_Lookup.Clear();
}
} }
} }
} }