public void Test_TRexSpatialMemoryCacheTests_EvictedItemsRemoval() { const int _originX = 123; const int _originY = 456; const int _size = 1000; // Create the cache with enough elements to hold one per context without eviction using (var cache = new TRexSpatialMemoryCache(1, 1000000, 0.5)) { // Make the number of contexts requested, and a separate item to be placed in each one var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint"); var item1 = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = _size, CacheOriginX = _originX, CacheOriginY = _originY }; var item2 = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = _size, CacheOriginX = _originX + SubGridTreeConsts.SubGridTreeDimension, CacheOriginY = _originY + SubGridTreeConsts.SubGridTreeDimension }; cache.Add(context, item1, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); cache.Add(context, item2, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); Assert.True(context.TokenCount == 1, "Token count not one after addition of second element forcing eviction of first"); Assert.True(cache.Get(context, item1.CacheOriginX, item1.CacheOriginY) == null, "Able to request item1 after it should have been evicted for item2"); Assert.True(cache.Get(context, item2.CacheOriginX, item2.CacheOriginY) != null, "Unable to request item2 after it should have replaced item1"); } }
public void Test_TRexSpatialMemoryCacheStorageTests_TimeCheckReusingElements(int numElements, int overflowBy) { var storage = new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(numElements, numElements / 2); var dummyCache = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(1000, 1000, 0), storage); var startTime = DateTime.Now; var item = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 1000, CacheOriginX = 2000, CacheOriginY = 3000 }; // Fill all available slots for (int i = 0; i < numElements; i++) { storage.Add(item, null); } var midTime = DateTime.Now; for (int i = 0; i < overflowBy; i++) { storage.Add(item, dummyCache); } Assert.False(true, $"Time for adding {numElements} elements is {midTime - startTime} and adding {overflowBy} overflows is {DateTime.Now - midTime}"); }
public void CacheContext_Pressure_MRULRUManagement() { using var cache = new TRexSpatialMemoryCache(100, 1000000, 0.1); // Create a context with default invalidation sensitivity, add some data to it // and validate that a change bitmask causes appropriate invalidation in concurrent operations var contextHeight = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprintHeight"); var contextPassCount = cache.LocateOrCreateContext(Guid.Empty, GridDataType.PassCount, "fingerprintPasscount"); var contexts = new[] { contextHeight, contextPassCount }; var itemsHeight = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; var itemsPassCount = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; var items = new[] { itemsHeight, itemsPassCount }; for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { items.ForEach((x, index) => x[i, j] = new TRexSpatialMemoryCacheContextTests_Element { Context = contexts[index], CacheOriginX = i * SubGridTreeConsts.SubGridTreeDimension, CacheOriginY = j * SubGridTreeConsts.SubGridTreeDimension, SizeInBytes = 1 }); } } // Progressively add elements in the cache forcing elements to be removed to accomodate them given the cache's small size // Check the retrieved element is also in the expected cache var additionTask = Task.Run(() => { for (var loopCount = 0; loopCount < 100; loopCount++) { for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { for (var contextIndex = 0; contextIndex < contexts.Length; contextIndex++) { TRexSpatialMemoryCacheContextTests_Element elem; if ((elem = (TRexSpatialMemoryCacheContextTests_Element)cache.Get(contexts[contextIndex], i, j)) != null) { elem.Context.Should().Be(contexts[contextIndex]); } else { cache.Add(contexts[contextIndex], items[contextIndex][i, j], contexts[contextIndex].InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); } } } } } }); Task.WaitAll(new[] { additionTask }); }
public void Test_TRexSpatialMemoryCacheContext_AddOneElement_FailWithInvalidationVersionMismatch() { ITRexSpatialMemoryCacheContext context = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(100, 1000000, 0.5), new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50)); var element = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 1000, CacheOriginX = 2000, CacheOriginY = 3000 }; context.OwnerMemoryCache.Add(context, element, context.InvalidationVersion + 1).Should().Be(CacheContextAdditionResult.RejectedDueToInvlidationVersionMismatch); }
public void Test_TRexCacheItem_Set() { var theObject = new TRexSpatialMemoryCacheContextTests_Element(); TRexCacheItem <TRexSpatialMemoryCacheContextTests_Element> item = new TRexCacheItem <TRexSpatialMemoryCacheContextTests_Element>(); item.Set(theObject, null, 100, 1, 2); Assert.True(ReferenceEquals(item.Item, theObject)); Assert.True(item.Prev == 1); Assert.True(item.Next == 2); Assert.True(item.MRUEpochToken == 100); }
public void Test_TRexCacheItem_Creation_Specific() { var storage = new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(10, 5); var dummyCache = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(1000, 1000, 0), storage); var theObject = new TRexSpatialMemoryCacheContextTests_Element(); TRexCacheItem <TRexSpatialMemoryCacheContextTests_Element> item = new TRexCacheItem <TRexSpatialMemoryCacheContextTests_Element>(theObject, dummyCache, 100, 1, 2); Assert.True(ReferenceEquals(item.Item, theObject)); Assert.True(item.Prev == 1); Assert.True(item.Next == 2); Assert.True(item.MRUEpochToken == 100); }
public void Test_TRexSpatialMemoryCacheContext_FailOverwriteOfExistingElement() { ITRexSpatialMemoryCacheContext context = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(100, 1000000, 0.5), new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50)); var element = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 1000, CacheOriginX = 2000, CacheOriginY = 3000 }; Assert.True(context.OwnerMemoryCache.Add(context, element, context.InvalidationVersion) == CacheContextAdditionResult.Added, "Result is false on addition of first element"); Assert.True(context.OwnerMemoryCache.Add(context, element, context.InvalidationVersion) == CacheContextAdditionResult.AlreadyExisting, "Result is true on second addition of same element"); }
public void Test_TRexSpatialMemoryCacheTests_ProductionDataIngestInvalidation() { using (var cache = new TRexSpatialMemoryCache(20000, 1000000, 0.5)) { // Create a context with default invalidation sensitivity, add some data to it // and validate that a change bitmask causes appropriate invalidation var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint"); var items = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { items[i, j] = new TRexSpatialMemoryCacheContextTests_Element { CacheOriginX = i * SubGridTreeConsts.SubGridTreeDimension, CacheOriginY = j * SubGridTreeConsts.SubGridTreeDimension, SizeInBytes = 1 }; cache.Add(context, items[i, j], context.InvalidationVersion); } } Assert.True(context.TokenCount == 10000, "Token count incorrect after addition"); // Create the bitmask ISubGridTreeBitMask mask = new SubGridTreeSubGridExistenceBitMask(); for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { mask[(i * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel, (j * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel] = true; } } cache.MRUList.TokenCount.Should().Be(context.TokenCount); cache.InvalidateDueToProductionDataIngest(Guid.Empty, Guid.NewGuid(), mask); cache.MRUList.TokenCount.Should().Be(0); } }
public void Test_TRexSpatialMemoryCacheContext_ReanimateViaItemAddition() { ITRexSpatialMemoryCacheContext context = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(100, 1000000, 0.5), new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50)); var element = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 1000, CacheOriginX = 2000, CacheOriginY = 3000 }; context.MarkForRemoval(DateTime.UtcNow); Assert.True(context.MarkedForRemoval, "Marking context for removal did not set state"); // Reanimate by adding an element Assert.True(context.OwnerMemoryCache.Add(context, element, context.InvalidationVersion) == CacheContextAdditionResult.Added, "Result is false on addition of first element"); Assert.False(context.MarkedForRemoval, "Marking context for removal did not set state"); Assert.True(context.MarkedForRemovalAtUtc == TRex.Common.Consts.MIN_DATETIME_AS_UTC, "Marking context for removal did not set date"); }
public void InvalidateSubGrid() { ITRexSpatialMemoryCacheContext context = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(100, 1000000, 0.5), new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50)); // context.InvalidateSubGrid facades context.InvalidateSubGrid context.InvalidateSubGrid(0, 0, out var present); present.Should().BeFalse(); var element = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 1000, CacheOriginX = 2000, CacheOriginY = 3000 }; context.OwnerMemoryCache.Add(context, element, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); context.InvalidateSubGrid(2000, 3000, out present); present.Should().BeTrue(); }
public void Test_TRexSpatialMemoryCacheTests_Remove() { using (var cache = new TRexSpatialMemoryCache(10, 1000000, 0.5)) { var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint"); var item = new TRexSpatialMemoryCacheContextTests_Element { CacheOriginX = 1000, CacheOriginY = 1000, SizeInBytes = 1000 }; cache.Add(context, item, context.InvalidationVersion); Assert.True(context.TokenCount == 1, "Token count incorrect after addition"); cache.Remove(context, item); Assert.True(context.TokenCount == 0, "Token count incorrect after removal"); } }
public void Test_TRexSpatialMemoryCacheStorageTests_GetElement() { var storage = new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50); var dummyCache = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(1000, 1000, 0), storage); var item = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 0, CacheOriginX = 2000, CacheOriginY = 3000 }; var index = storage.Add(item, dummyCache); Assert.True(storage.TokenCount == 1, $"Element count incorrect (= {storage.TokenCount})"); var getItem = storage.Get(index); Assert.True(ReferenceEquals(item, getItem), "Item retrieved from storage not same as item placed in storage"); }
public void Test_TRexSpatialMemoryCacheContext_AddOneElement() { ITRexSpatialMemoryCacheContext context = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(100, 1000000, 0.5), new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50)); var element = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 1000, CacheOriginX = 2000, CacheOriginY = 3000 }; context.OwnerMemoryCache.Add(context, element, context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); Assert.True(context.TokenCount == 1, $"Element count incorrect (= {context.TokenCount})"); Assert.True(context.MRUList.TokenCount == 1, $"MRU list count incorrect (= {context.MRUList.TokenCount})"); // Check the newly added element in the context is present in the context map with a 1-based index int token = context.ContextTokens[element.CacheOriginX >> SubGridTreeConsts.SubGridIndexBitsPerLevel, element.CacheOriginY >> SubGridTreeConsts.SubGridIndexBitsPerLevel]; Assert.True(token == 1, "Single newly added element does not have index of 1 present in ContextTokens"); }
public void Test_TRexSpatialMemoryCacheContext_MarkForRemovalViaLastElementRemoved() { ITRexSpatialMemoryCacheContext context = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(100, 1000000, 0.5), new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50)); Assert.False(context.MarkedForRemoval); var currentDate = DateTime.UtcNow; var element = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 1000, CacheOriginX = 2000, CacheOriginY = 3000 }; Assert.True(context.OwnerMemoryCache.Add(context, element, context.InvalidationVersion) == CacheContextAdditionResult.Added, "Result is false on addition of first element"); Assert.True(!context.MarkedForRemoval && context.TokenCount == 1); context.Remove(element); Assert.True(context.MarkedForRemoval, "Removing last element in context for removal did not mark for removal"); Assert.True(context.MarkedForRemovalAtUtc >= currentDate, "Removal date not expected"); }
public void Test_TRexSpatialMemoryCacheStorageTests_GetAfterRemove() { var storage = new TRexSpatialMemoryCacheStorage <ITRexMemoryCacheItem>(100, 50); var dummyCache = new TRexSpatialMemoryCacheContext(new TRexSpatialMemoryCache(1000, 1000, 0), storage); var item = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = 0, CacheOriginX = 2000, CacheOriginY = 3000 }; var index = storage.Add(item, dummyCache); Assert.True(storage.TokenCount == 1, $"Element count incorrect after add (= {storage.TokenCount})"); Assert.True(storage.Get(index) != null); // Remove the item storage.Remove(index); Assert.True(storage.Get(index) == null, "Was able to extract item after invalidation"); Assert.True(storage.TokenCount == 0, $"Element count incorrect after get after invalidation(= {storage.TokenCount})"); }
public void Test_TRexSpatialMemoryCacheTests_ItemReplacement() { const int _originX = 123; const int _originY = 456; const int _size = 1000; // Create the cache with enough elements to hold one per context without eviction using (var cache = new TRexSpatialMemoryCache(1, 1000000, 0.5)) { // Make the number ofg contexts requested, and a separate item to be placed in each one var context = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprint"); var item1 = new TRexSpatialMemoryCacheContextTests_Element { SizeInBytes = _size, CacheOriginX = _originX, CacheOriginY = _originY }; Assert.True(cache.Add(context, item1, context.InvalidationVersion) == CacheContextAdditionResult.Added, "Failed to add element for the first time"); Assert.False(cache.Add(context, item1, context.InvalidationVersion) == CacheContextAdditionResult.Added, "Succeeded overwriting element - bad!"); } }
public void Test_TRexSpatialMemoryCacheTests_ConcurrentAccessWithDesignAndProductionDataIngestInvalidation() { using var cache = new TRexSpatialMemoryCache(20000, 1000000, 0.5); // Create a context with default invalidation sensitivity, add some data to it // and validate that a change bitmask causes appropriate invalidation in concurrent operations var contextHeight = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprintHeight"); var contextPassCount = cache.LocateOrCreateContext(Guid.Empty, GridDataType.PassCount, "fingerprintPasscount"); var contexts = new[] { contextHeight, contextPassCount }; var itemsHeight = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; var itemsPassCount = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; var items = new[] { itemsHeight, itemsPassCount }; // Create the bitmask ISubGridTreeBitMask mask = new SubGridTreeSubGridExistenceBitMask(); for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { mask[(i * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel, (j * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel] = true; items.ForEach(x => x[i, j] = new TRexSpatialMemoryCacheContextTests_Element { CacheOriginX = i * SubGridTreeConsts.SubGridTreeDimension, CacheOriginY = j * SubGridTreeConsts.SubGridTreeDimension, SizeInBytes = 1 }); } } var additionComplete = false; var invalidationComplete = false; // Progressively add and invalidate the elements in the cache in separate tasks var additionTask = Task.Run(() => { for (var loopCount = 0; loopCount < 100; loopCount++) { for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { for (var contextIndex = 0; contextIndex < contexts.Length; contextIndex++) { if (cache.Get(contexts[contextIndex], i, j) == null) { cache.Add(contexts[contextIndex], items[contextIndex][i, j], contexts[contextIndex].InvalidationVersion); } } } } } additionComplete = true; }); var invalidationTask = Task.Run(() => { for (var loopCount = 0; loopCount < 100; loopCount++) { for (var i = 99; i >= 0; i--) { for (var j = 99; j >= 0; j--) { foreach (var context in contexts) { // Empty contexts are ignored if (context.TokenCount > 0) { context.InvalidateSubGrid(i * SubGridTreeConsts.SubGridTreeDimension, j * SubGridTreeConsts.SubGridTreeDimension, out var subGridPresentForInvalidation); } } } } } invalidationComplete = true; }); var numDesignInvalidations = 0; var designUpdateTask = Task.Run(async() => { while (!additionComplete || !invalidationComplete) { // Mimic design invalidation by periodically invalidating all sub grids await Task.Delay(10); contexts[numDesignInvalidations % contexts.Length].InvalidateAllSubGrids(); numDesignInvalidations++; } }); Task.WaitAll(new[] { additionTask, invalidationTask, designUpdateTask }); numDesignInvalidations.Should().BeGreaterThan(0); }
public void Test_TRexSpatialMemoryCacheTests_DesignChangeInvalidation() { var projectUid = Guid.NewGuid(); var gridDataType = Types.GridDataType.PassCount; var designUid = Guid.NewGuid(); var designUid2 = Guid.NewGuid(); Guid[] includedSurveyedSurfaces = new Guid[] { designUid }; Guid[] includedSurveyedSurfaces2 = new Guid[] { designUid2 }; using (var cache = new TRexSpatialMemoryCache(20000, 1000000, 0.5)) { // Create a context with a included design and validate that a design change causes the appropriate invalidation var testGuid = Guid.NewGuid(); var context = cache.LocateOrCreateContext(projectUid, gridDataType, SpatialCacheFingerprint.ConstructFingerprint(projectUid, gridDataType, null, includedSurveyedSurfaces)); // this content will remain in cache as it uses a different design var context2 = cache.LocateOrCreateContext(projectUid, gridDataType, SpatialCacheFingerprint.ConstructFingerprint(projectUid, gridDataType, null, includedSurveyedSurfaces2)); Assert.True(context.MarkedForRemoval == true, "Empty contents should be marked for removal"); TRexSpatialMemoryCacheContextTests_Element[,] items = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; TRexSpatialMemoryCacheContextTests_Element[,] items2 = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; // Add content to our 2 contexts for (var k = 1; k < 3; k++) { for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { if (k == 1) { items[i, j] = new TRexSpatialMemoryCacheContextTests_Element { CacheOriginX = (int)(i * SubGridTreeConsts.SubGridTreeDimension), CacheOriginY = (int)(j * SubGridTreeConsts.SubGridTreeDimension), SizeInBytes = 1 }; cache.Add(context, items[i, j], context.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added);; } else { items2[i, j] = new TRexSpatialMemoryCacheContextTests_Element { CacheOriginX = (int)(i * SubGridTreeConsts.SubGridTreeDimension), CacheOriginY = (int)(j * SubGridTreeConsts.SubGridTreeDimension), SizeInBytes = 1 }; cache.Add(context2, items2[i, j], context2.InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); } } } } // verify items added OK Assert.True(context.MarkedForRemoval == false, "Context should not be marked for removal"); Assert.True(context.TokenCount == 10000, "Token count incorrect after addition"); Assert.True(context2.MarkedForRemoval == false, "Context2 should not be marked for removal"); Assert.True(context2.TokenCount == 10000, "Token count incorrect after addition of context2"); // invalidate first of the 2 contexts added cache.InvalidateDueToDesignChange(projectUid, designUid); cache.MRUList.TokenCount.Should().Be(10000); int counter = 0; for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { cache.MRUList.Get(counter++).Should().NotBe(items[i, j]); } } for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { cache.MRUList.Get(counter++).Should().Be(items2[i, j]); } } //Test context is removed Assert.True(context.MarkedForRemoval == true, "Empty context should be marked for removal"); Assert.True(context.TokenCount == 0, "Token count incorrect after invalidation"); // Test context2 remains Assert.True(context2.MarkedForRemoval == false, "Context2 should not be marked for removal"); Assert.True(context2.TokenCount == 10000, "Token count incorrect after test"); } }
public void CacheContext_Tenancy_IsUpheld_OnConcurrentRequests_WithLRUEvictionOnSmallCacheSize() { using var cache = new TRexSpatialMemoryCache(100, 1000000, 0.1); // Create a context with default invalidation sensitivity, add some data to it // and validate that a change bitmask causes appropriate invalidation in concurrent operations var contextHeight = cache.LocateOrCreateContext(Guid.Empty, GridDataType.Height, "fingerprintHeight"); var contextPassCount = cache.LocateOrCreateContext(Guid.Empty, GridDataType.PassCount, "fingerprintPasscount"); var contexts = new[] { contextHeight, contextPassCount }; var itemsHeight = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; var itemsPassCount = new TRexSpatialMemoryCacheContextTests_Element[100, 100]; var items = new[] { itemsHeight, itemsPassCount }; // Create the bitmask ISubGridTreeBitMask mask = new SubGridTreeSubGridExistenceBitMask(); for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { mask[(i * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel, (j * SubGridTreeConsts.SubGridTreeDimension) >> SubGridTreeConsts.SubGridIndexBitsPerLevel] = true; items.ForEach((x, index) => x[i, j] = new TRexSpatialMemoryCacheContextTests_Element { Context = contexts[index], CacheOriginX = i * SubGridTreeConsts.SubGridTreeDimension, CacheOriginY = j * SubGridTreeConsts.SubGridTreeDimension, SizeInBytes = 1 }); } } // var additionComplete = false; // var invalidationComplete = false; // Progressively add elements in the cache forcing elements to be removed to accomodate them given the cache's small size var additionTask = Task.Run(() => { for (var loopCount = 0; loopCount < 100; loopCount++) { for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { for (var contextIndex = 0; contextIndex < contexts.Length; contextIndex++) { TRexSpatialMemoryCacheContextTests_Element elem; if ((elem = (TRexSpatialMemoryCacheContextTests_Element)cache.Get(contexts[contextIndex], i, j)) != null) { elem.Context.Should().Be(contexts[contextIndex]); } else { cache.Add(contexts[contextIndex], items[contextIndex][i, j], contexts[contextIndex].InvalidationVersion).Should().Be(CacheContextAdditionResult.Added); } } } } } // additionComplete = true; }); /* * var invalidationTask = Task.Run(() => * { * for (var loopCount = 0; loopCount < 100; loopCount++) * { * for (var i = 99; i >= 0; i--) * { * for (var j = 99; j >= 0; j--) * { * foreach (var context in contexts) * { * // Empty contexts are ignored * if (context.TokenCount > 0) * { * context.InvalidateSubGrid(i * SubGridTreeConsts.SubGridTreeDimension, j * SubGridTreeConsts.SubGridTreeDimension, out var subGridPresentForInvalidation); * } * } * } * } * } * * invalidationComplete = true; * }); */ /* var numDesignInvalidations = 0; * var designUpdateTask = Task.Run(async () => * { * while (!additionComplete || !invalidationComplete) * { * // Mimic design invalidation by periodically invalidating all sub grids * await Task.Delay(10); * contexts[numDesignInvalidations % contexts.Length].InvalidateAllSubGrids(); * * numDesignInvalidations++; * } * }); * */ Task.WaitAll(new[] { additionTask /*, invalidationTask, designUpdateTask*/ }); // numDesignInvalidations.Should().BeGreaterThan(0); }