|
using System;
using System.Collections.Generic;
using Pfz.Caching;
using Pfz.Extensions;
namespace Pfz.DynamicObjects.CloneToModifyModel
{
/// <summary>
/// Represents a local (non-shared) cache of Ctm objects.
/// </summary>
/// <typeparam name="TKey">The type of the key of the cache.</typeparam>
/// <typeparam name="TItem">The type of the items in the cache.</typeparam>
public sealed class CtmLocalCache<TKey, TItem>
where
TItem: class, ICtmCacheable<TKey>
{
private readonly CtmGlobalCache<TKey, TItem> _globalCache;
private readonly Dictionary<TKey, KeyValuePair<TItem, TItem>> _dictionary = new Dictionary<TKey, KeyValuePair<TItem, TItem>>();
private readonly HashSet<TKey> _updated = new HashSet<TKey>();
private readonly Dictionary<TKey, TItem> _dictionaryReadOnly = new Dictionary<TKey, TItem>();
private readonly List<TItem> _newReadOnly = new List<TItem>();
internal CtmLocalCache(CtmGlobalCache<TKey, TItem> globalCache)
{
_globalCache = globalCache;
}
/// <summary>
/// Clears the cache of modifiable items. Read-only items
/// are kept.
/// </summary>
public void Clear()
{
_dictionary.Clear();
_updated.Clear();
}
/// <summary>
/// Clears both modifiable and read-only items.
/// </summary>
public void ClearAll()
{
_dictionary.Clear();
_updated.Clear();
_dictionaryReadOnly.Clear();
_newReadOnly.Clear();
}
/// <summary>
/// Gets an item by key. If it is not found in the local cache,
/// the global cache is searched. May return null if it is
/// simple not in the cache at all.
/// </summary>
public TItem Get(TKey key)
{
TItem result;
KeyValuePair<TItem, TItem> pair;
if (_dictionary.TryGetValue(key, out pair))
{
result = pair.Value;
GCUtils.KeepAlive(result);
return result;
}
if (_dictionaryReadOnly.TryGetValue(key, out result))
{
GCUtils.KeepAlive(result);
return result;
}
result = _globalCache._Get(key);
if (result != null)
{
GCUtils.KeepAlive(result);
bool isNonUpdatable = result.GetCtmType().IsNonUpdatable;
if (isNonUpdatable)
_dictionaryReadOnly.Add(key, result);
else
{
pair = new KeyValuePair<TItem, TItem>(result, result);
_dictionary.Add(key, pair);
}
}
return result;
}
/// <summary>
/// Merges this cache with the global cache.
/// Conflicting items (like items that may be updated by other threads) are
/// simple removed from the cache.
/// </summary>
public void Merge()
{
if (_updated.Count > 0)
{
_globalCache._modifiableItemsLock.EnterWriteLock();
try
{
var globalModifiable = _globalCache._modifiableItems;
foreach(var key in _updated)
{
var pair = _dictionary[key];
var oldItem = pair.Key;
var item = pair.Value;
var other = globalModifiable[key];
if (other == null || other.Equals(oldItem))
{
globalModifiable[key] = item;
_dictionary[key] = new KeyValuePair<TItem, TItem>(item, item);
}
else
{
globalModifiable.Remove(key);
_dictionary.Remove(key);
}
}
}
finally
{
_globalCache._modifiableItemsLock.ExitWriteLock();
}
_updated.Clear();
}
var globalReadOnly = _globalCache._readOnlyItems;
foreach(var item in _newReadOnly)
{
var key = item.GetCacheKey();
var other = globalReadOnly.GetOrCreateValue(key, (k) => item);
if (other != item)
{
if (!item.Equals(other))
throw new InvalidOperationException("The cache is corrupted. There are two different non-updatable items in the cache.");
_dictionaryReadOnly[key] = other;
}
}
_newReadOnly.Clear();
}
/// <summary>
/// Puts the given item in the cache and returns the read-only
/// version of it (if it is not already read-only). Future updates
/// before clearing the cache must use the given return as the source.
/// </summary>
public TItem Set(TItem item)
{
if (item == null)
throw new ArgumentNullException("item");
var key = item.GetCacheKey();
TItem oldItem;
bool isNonUpdatable = item.GetCtmType().IsNonUpdatable;
if (isNonUpdatable)
{
if (!_dictionaryReadOnly.TryGetValue(key, out oldItem))
{
item = item.AsReadOnly(false);
_dictionaryReadOnly.Add(key, item);
_newReadOnly.Add(item);
return item;
}
if (!item.Equals(oldItem))
throw new InvalidOperationException("Another non-updatable item with different values was already put into the cache.");
return oldItem;
}
KeyValuePair<TItem, TItem> pair;
if (_dictionary.TryGetValue(key, out pair))
{
var oldInstance = item.GetOldInstance();
if (oldInstance == null)
throw new InvalidOperationException("You are putting a new item in the cache but there is one already there. If you are updating, create a modifiable clone from the read-only one.");
oldItem = pair.Value;
if (oldInstance != oldItem && !oldInstance.Equals(oldItem))
throw new InvalidOperationException("The actual item does not has an old item compatible with the one in the cache. There are possible problems with your transactions.");
GCUtils.Expire(oldItem);
}
item = item.AsReadOnly(false);
pair = new KeyValuePair<TItem,TItem>(pair.Key, item);
_dictionary[key] = pair;
_updated.Add(key);
return item;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.
At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.
Want more info or simply want to contact me?
Take a look at:
http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com
Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).