NuCache: fix panic exception
This commit is contained in:
@@ -1098,6 +1098,87 @@ namespace Umbraco.Tests.Cache
|
||||
Assert.AreEqual("four", all[3]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DontPanic()
|
||||
{
|
||||
var d = new SnapDictionary<int, string>();
|
||||
d.Test.CollectAuto = false;
|
||||
|
||||
Assert.IsNull(d.Test.GenObj); // set with first snapshot or first lock, then never null
|
||||
|
||||
// gen 1
|
||||
d.Set(1, "one");
|
||||
Assert.IsTrue(d.Test.NextGen);
|
||||
Assert.AreEqual(1, d.Test.LiveGen);
|
||||
Assert.IsNotNull(d.Test.GenObj); // set with lock
|
||||
|
||||
var s1 = d.CreateSnapshot();
|
||||
Assert.IsFalse(d.Test.NextGen);
|
||||
Assert.AreEqual(1, d.Test.LiveGen);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(1, d.Test.GenObj.Gen);
|
||||
|
||||
Assert.AreEqual(1, s1.Gen);
|
||||
Assert.AreEqual("one", s1.Get(1));
|
||||
|
||||
d.Set(1, "uno");
|
||||
Assert.IsTrue(d.Test.NextGen);
|
||||
Assert.AreEqual(2, d.Test.LiveGen);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(1, d.Test.GenObj.Gen);
|
||||
|
||||
var scopeContext = new ScopeContext();
|
||||
var scopeProvider = GetScopeProvider(scopeContext);
|
||||
|
||||
// scopeProvider.Context == scopeContext -> writer is scoped
|
||||
// writer is scope contextual and scoped
|
||||
// when disposed, nothing happens
|
||||
// when the context exists, the writer is released
|
||||
using (d.GetWriter(scopeProvider))
|
||||
{
|
||||
d.Set(1, "ein");
|
||||
Assert.IsTrue(d.Test.NextGen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(2, d.Test.GenObj.Gen);
|
||||
}
|
||||
|
||||
// writer has not released
|
||||
Assert.AreEqual(1, d.Test.WLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(2, d.Test.GenObj.Gen);
|
||||
|
||||
// nothing changed
|
||||
Assert.IsTrue(d.Test.NextGen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
|
||||
// panic!
|
||||
var s2 = d.CreateSnapshot();
|
||||
|
||||
Assert.AreEqual(1, d.Test.WLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(2, d.Test.GenObj.Gen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
Assert.IsTrue(d.Test.NextGen);
|
||||
|
||||
// release writer
|
||||
scopeContext.ScopeExit(true);
|
||||
|
||||
Assert.AreEqual(0, d.Test.WLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(2, d.Test.GenObj.Gen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
Assert.IsTrue(d.Test.NextGen);
|
||||
|
||||
var s3 = d.CreateSnapshot();
|
||||
|
||||
Assert.AreEqual(0, d.Test.WLocked);
|
||||
Assert.IsNotNull(d.Test.GenObj);
|
||||
Assert.AreEqual(3, d.Test.GenObj.Gen);
|
||||
Assert.AreEqual(3, d.Test.LiveGen);
|
||||
Assert.IsFalse(d.Test.NextGen);
|
||||
}
|
||||
|
||||
private IScopeProvider GetScopeProvider(ScopeContext scopeContext = null)
|
||||
{
|
||||
var scopeProvider = Mock.Of<IScopeProvider>();
|
||||
|
||||
@@ -133,11 +133,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
{
|
||||
_wlocked++;
|
||||
lockInfo.Count = true;
|
||||
if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects
|
||||
if (_nextGen == false || (forceGen && _wlocked == 1))
|
||||
{
|
||||
// because we are changing things, a new generation
|
||||
// is created, which will trigger a new snapshot
|
||||
_nextGen = true;
|
||||
_genObjs.Enqueue(_genObj = new GenObj(_liveGen));
|
||||
_liveGen += 1;
|
||||
}
|
||||
}
|
||||
@@ -214,7 +215,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
else
|
||||
dictionary.TryUpdate(key, link.Next, link);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -139,18 +139,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
_wlocked++;
|
||||
lockInfo.Count = true;
|
||||
|
||||
// fixme - this comment: "ok to have holes in generation objects" is annoying
|
||||
// 'cos if you have a hole, then doing _liveGen-1 in some places would be wrong
|
||||
// besides, forceGen is used only when getting a scoped write lock, which is not reentrant?
|
||||
//
|
||||
// but... if _wlocked ==1 and we just incremented it, it means it was 0, so it wasnt locked, so wtf?
|
||||
// this is the only place where _nextGen would turn true so ... this makes no sense at all!
|
||||
|
||||
if (_nextGen == false || (forceGen && _wlocked == 1)) // if true already... ok to have "holes" in generation objects
|
||||
if (_nextGen == false || (forceGen && _wlocked == 1))
|
||||
{
|
||||
// because we are changing things, a new generation
|
||||
// is created, which will trigger a new snapshot
|
||||
_nextGen = true; // this is the ONLY place where _nextGen becomes true
|
||||
_genObjs.Enqueue(_genObj = new GenObj(_liveGen));
|
||||
_liveGen += 1;
|
||||
}
|
||||
}
|
||||
@@ -379,9 +373,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
if (_genObj == null)
|
||||
_genObjs.Enqueue(_genObj = new GenObj(snapGen));
|
||||
|
||||
// fixme - getting a panic exception here
|
||||
// means _genObj != null, means _nextGen == true
|
||||
|
||||
// if we have one already, ensure it's consistent
|
||||
else if (_genObj.Gen != snapGen)
|
||||
throw new Exception("panic");
|
||||
@@ -551,6 +542,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
|
||||
set => _dict._collectAuto = value;
|
||||
}
|
||||
|
||||
public GenObj GenObj => _dict._genObj;
|
||||
|
||||
public ConcurrentQueue<GenObj> GenObjs => _dict._genObjs;
|
||||
|
||||
public Snapshot LiveSnapshot => new Snapshot(_dict, _dict._liveGen);
|
||||
|
||||
Reference in New Issue
Block a user