using System.Collections.Immutable; namespace HermesSocketServer.Store.Internal { public abstract class GroupSaveStore : IStore where K : class where V : class { protected readonly IDictionary _store; protected readonly IList _added; protected readonly IList _modified; protected readonly IList _deleted; protected readonly ReaderWriterLockSlim _rwls; public GroupSaveStore() { _store = new Dictionary(); _added = new List(); _modified = new List(); _deleted = new List(); _rwls = new ReaderWriterLockSlim(); } public abstract Task Load(); protected abstract void OnInitialAdd(K key, V value); protected abstract void OnInitialModify(K key, V value, V newValue); protected abstract void OnPostRemove(K key, V value); public abstract Task Save(); public bool Exists(K key) { _rwls.EnterReadLock(); try { return _store.ContainsKey(key); } finally { _rwls.ExitReadLock(); } } public V? Get(K key) { _rwls.EnterReadLock(); try { if (_store.TryGetValue(key, out var value)) return value; } finally { _rwls.ExitReadLock(); } return null; } public IDictionary Get() { _rwls.EnterReadLock(); try { return _store.ToImmutableDictionary(); } finally { _rwls.ExitReadLock(); } } public bool Modify(K? key, V value) { if (key == null) return false; _rwls.EnterUpgradeableReadLock(); try { if (_store.TryGetValue(key, out V? oldValue)) { _rwls.EnterWriteLock(); try { OnInitialModify(key, oldValue, value); _store[key] = value; if (!_added.Contains(key) && !_modified.Contains(key)) { _modified.Add(key); } return true; } finally { _rwls.ExitWriteLock(); } } } finally { _rwls.ExitUpgradeableReadLock(); } return false; } public bool Modify(K? key, Action modify) { if (key == null) return false; _rwls.EnterUpgradeableReadLock(); try { if (_store.TryGetValue(key, out V? value)) { _rwls.EnterWriteLock(); try { modify(value); if (!_added.Contains(key) && !_modified.Contains(key)) { _modified.Add(key); } return true; } finally { _rwls.ExitWriteLock(); } } } finally { _rwls.ExitUpgradeableReadLock(); } return false; } public bool Remove(K? key, bool fromCascade = false) { if (key == null) return false; _rwls.EnterWriteLock(); try { if (_store.TryGetValue(key, out var value)) { _store.Remove(key); OnPostRemove(key, value); if (!_added.Remove(key)) { _modified.Remove(key); if (!fromCascade && !_deleted.Contains(key)) { _deleted.Add(key); } } return true; } } finally { _rwls.ExitWriteLock(); } return false; } public bool Set(K? key, V value) { if (key == null) return false; _rwls.EnterWriteLock(); try { if (_store.TryGetValue(key, out V? fetched)) { OnInitialModify(key, fetched, value); _store[key] = value; if (!_added.Contains(key) && !_modified.Contains(key)) { _modified.Add(key); } return true; } else { OnInitialAdd(key, value); _store.Add(key, value); if (!_deleted.Remove(key) && !_added.Contains(key)) { _added.Add(key); } return true; } } finally { _rwls.ExitWriteLock(); } } } }