using System; using System.Collections.Generic; using System.Text; namespace Thousandto.Core.Base { public interface IGcSweepingStrategy { bool Sweepable(); bool Sweepable(object value); } public interface IGcGenerationStrategy { int GetMaxGenerationCount(); bool NextGeneration(int gen, int lived); bool Collectable(int gen, int last); } public class GcGenerationStrategy : IGcGenerationStrategy { public const int MAX_GENERATION_INDEX = 32; public static int Now() { return System.Environment.TickCount; } public int GetMaxGenerationCount() { return MAX_GENERATION_INDEX; } public bool NextGeneration(int gen, int lived) { return lived > 60000 * gen; } public bool Collectable(int gen, int last) { return Now() - last > 60000 * gen; } public static GcGenerationStrategy sharedInstance = new GcGenerationStrategy(); } public class GcSweepingStrategy : IGcSweepingStrategy { int _past = 0; int _interval = 0; internal const int DefaultInterval = 8000; //8s public GcSweepingStrategy(int intervalMS = 0) { if (intervalMS <= 0) { intervalMS = DefaultInterval; } _interval = intervalMS; _past = System.Environment.TickCount; } public virtual bool Sweepable(object value) { return true; } public virtual bool Sweepable() { bool result = false; int now = System.Environment.TickCount; if (now - _past > _interval) { result = true; _past = now; } return result; } } public enum DataCacheCollectableFlag { None = 0, No = 1, Yes = 2, Collectable = 4, Touching = 8, } public class DataCache { class Item { internal TValue value = default(TValue); internal int lastTouchedTime = 0; internal int livedTime = 0; internal int generation = 0; internal bool immortal = false; internal DataCache owner; internal void Assign(TValue source, int gen = 1, int now = 0) { if (now <= 0) { now = GcGenerationStrategy.Now(); } value = source; generation = gen; lastTouchedTime = now; livedTime = 0; } internal void Touch() { var n = GcGenerationStrategy.Now(); var e = n - lastTouchedTime; lastTouchedTime = n; livedTime += e; if (owner._gen.NextGeneration(generation, livedTime) && generation + 1 <= owner._gen.GetMaxGenerationCount()) { ++generation; } } internal Item(TValue source, DataCache _owner) { owner = _owner; Assign(source, 1, GcGenerationStrategy.Now()); } internal bool Collectable() { return immortal == false && owner._gen.Collectable(generation, lastTouchedTime); } } IGcSweepingStrategy _sweeper = null; IGcGenerationStrategy _gen = null; MyAction _releaseValueFunc = null; MyFunc _collectableFunc = null; Dictionary _tank = null; Dictionary tank { get { if (_tank == null) { _tank = new Dictionary(); } return _tank; } } public DataCache(int sweepIntervalMS = -1, IGcSweepingStrategy sweeper = null, IGcGenerationStrategy gen = null, MyAction valueReleaser = null, MyFunc collectableFunc = null) { _sweeper = sweeper; _releaseValueFunc = valueReleaser; _collectableFunc = collectableFunc; if (_sweeper == null) { if (sweepIntervalMS < 0) { sweepIntervalMS = GcSweepingStrategy.DefaultInterval; } _sweeper = new GcSweepingStrategy(sweepIntervalMS); } _gen = gen ?? GcGenerationStrategy.sharedInstance; } ~DataCache() { Clear(); } Item GetItem(TKey key) { Item item = null; if (_tank != null && _tank.TryGetValue(key, out item)) { item.Touch(); return item; } else { return null; } } void _ReleaseValue(TValue value) { if (_releaseValueFunc != null) { _releaseValueFunc(value); value = default(TValue); } } int _DoSweep(bool force) { int result = 0; if (_tank != null) { List _removeList = null; var e = _tank.GetEnumerator(); try { while (e.MoveNext()) { var item = e.Current; if (force) { item.Value.immortal = false; } if (item.Value.Collectable()) { if (_collectableFunc != null) { var ret = _collectableFunc(item.Value.value); if (ret != DataCacheCollectableFlag.None) { if ((ret & DataCacheCollectableFlag.No) != 0) { if ((ret & DataCacheCollectableFlag.Touching) != 0) { item.Value.Touch(); } continue; } } } if (_removeList == null) { _removeList = new List(); } _ReleaseValue(item.Value.value); _removeList.Add(item.Key); } } } finally { e.Dispose(); } if (_removeList != null) { for (int i = 0; i < _removeList.Count; ++i) { if (_tank.Remove(_removeList[i])) { result += 1; } } } } return result; } public bool SetImmortal(TKey key, bool enable = true) { var item = GetItem(key); if (item != null) { item.immortal = enable; return true; } return false; } public TValue Get(TKey key, bool sweep = true) { var item = GetItem(key); var ret = item != null ? item.value : default(TValue); if (sweep) { Sweep(); } return ret; } public bool Remove(TKey key) { Item item = null; if (_tank != null && _tank.TryGetValue(key, out item)) { if (_releaseValueFunc != null) { _releaseValueFunc(item.value); } return _tank.Remove(key); } return false; } public bool Add(TKey key, TValue value) { if (value == null) { return false; } var dict = tank; Item item = null; if (dict.TryGetValue(key, out item)) { item.Touch(); return false; } item = new Item(value, this); dict.Add(key, item); return true; } public bool TryGetValue(TKey key, out TValue value, bool sweep = true) { if (_tank != null) { Item item = null; if (_tank.TryGetValue(key, out item)) { item.Touch(); value = item.value; if (sweep) { Sweep(); } return true; } } value = default(TValue); return false; } public void Clear() { if (_tank != null) { if (_releaseValueFunc != null) { var e = _tank.GetEnumerator(); try { while (e.MoveNext()) { _releaseValueFunc(e.Current.Value.value); } } finally { e.Dispose(); } } _tank.Clear(); _tank = null; } } public int Count { get { if (_tank != null) { return _tank.Count; } return 0; } } public int ForEachValue(MyAction func) { int count = 0; if (_tank != null) { var e = _tank.GetEnumerator(); try { while (e.MoveNext()) { func(e.Current.Value.value); ++count; } } finally { e.Dispose(); } } return count; } public int ForEach(MyAction func, TCtx ctx = null) where TCtx : class { int count = 0; if (_tank != null) { var e = _tank.GetEnumerator(); try { while (e.MoveNext()) { func(e.Current.Key, e.Current.Value.value, ctx); ++count; } } finally { e.Dispose(); } } return count; } public int Clear(MyFunc func) { int result = 0; if (_tank != null) { List _removeList = null; var e = _tank.GetEnumerator(); try { while (e.MoveNext()) { var item = e.Current; bool skip = func(item.Value.generation, item.Value.lastTouchedTime, item.Value.livedTime); if (!skip) { if (_removeList == null) { _removeList = new List(); } _ReleaseValue(item.Value.value); _removeList.Add(item.Key); } } } finally { e.Dispose(); } if (_removeList != null) { for (int i = 0; i < _removeList.Count; ++i) { if (_tank.Remove(_removeList[i])) { result += 1; } } } } return result; } public int Sweep(bool force = false) { int result = 0; if (force || _sweeper.Sweepable()) { result = _DoSweep(force); } return result; } } public class DataCacheLit : DataCache { public bool Add(TKey key, TValue value) { return base.Add(key, value); } public bool TryGetValue(TKey key, out TValue value, bool sweep = true) { object _value = null; value = default(TValue); var r = base.TryGetValue(key, out _value, sweep); if (r) { try { value = (TValue)_value; } catch (Exception e) { System.Diagnostics.Trace.Fail(e.Message,e.StackTrace); } } return r; } } }