Imported Upstream version 4.8.0.309

Former-commit-id: 5f9c6ae75f295e057a7d2971f3a6df4656fa8850
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-11-10 13:04:39 +00:00
parent ee1447783b
commit 94b2861243
4912 changed files with 390737 additions and 49310 deletions

View File

@@ -27,7 +27,7 @@ namespace System.Runtime.Caching {
private long[] _cacheSizeSamples;
private DateTime[] _cacheSizeSampleTimes;
private int _idx;
private SRef _sizedRef;
private SRefMultiple _sizedRefMultiple;
private int _gen2Count;
private long _memoryLimit;
@@ -51,7 +51,7 @@ namespace System.Runtime.Caching {
private void InitDisposableMembers(int cacheMemoryLimitMegabytes) {
bool dispose = true;
try {
_sizedRef = new SRef(_memoryCache);
_sizedRefMultiple = new SRefMultiple(_memoryCache.AllSRefTargets);
SetLimit(cacheMemoryLimitMegabytes);
InitHistory();
dispose = false;
@@ -112,8 +112,8 @@ namespace System.Runtime.Caching {
}
public void Dispose() {
SRef sref = _sizedRef;
if (sref != null && Interlocked.CompareExchange(ref _sizedRef, null, sref) == sref) {
SRefMultiple sref = _sizedRefMultiple;
if (sref != null && Interlocked.CompareExchange(ref _sizedRefMultiple, null, sref) == sref) {
sref.Dispose();
}
IMemoryCacheManager memoryCacheManager = s_memoryCacheManager;
@@ -139,7 +139,7 @@ namespace System.Runtime.Caching {
// This update must happen, otherwise the CacheManager won't
// know the total cache size.
int gen2Count = GC.CollectionCount(2);
SRef sref = _sizedRef;
SRefMultiple sref = _sizedRefMultiple;
if (gen2Count != _gen2Count && sref != null) {
// update _gen2Count
_gen2Count = gen2Count;

View File

@@ -27,9 +27,8 @@ namespace System.Runtime.Caching {
private static object s_initLock = new object();
private static MemoryCache s_defaultCache;
private static CacheEntryRemovedCallback s_sentinelRemovedCallback = new CacheEntryRemovedCallback(SentinelEntry.OnCacheEntryRemovedCallback);
private MemoryCacheStore[] _stores;
private GCHandleRef<MemoryCacheStore>[] _storeRefs;
private int _storeCount;
private int _storeMask;
private int _disposed;
private MemoryCacheStatistics _stats;
private string _name;
@@ -150,8 +149,18 @@ namespace System.Runtime.Caching {
if (hashCode < 0) {
hashCode = (hashCode == Int32.MinValue) ? 0 : -hashCode;
}
int idx = hashCode & _storeMask;
return _stores[idx];
int idx = hashCode % _storeCount;
return _storeRefs[idx].Target;
}
internal object[] AllSRefTargets {
get {
var allStores = new MemoryCacheStore[_storeCount];
for (int i = 0; i < _storeCount; i++) {
allStores[i] = _storeRefs[i].Target;
}
return allStores;
}
}
[SecuritySafeCritical]
@@ -166,8 +175,8 @@ namespace System.Runtime.Caching {
catch {
// ignore exceptions from perf counters
}
for (int i = 0; i < _stores.Length; i++) {
_stores[i] = new MemoryCacheStore(this, _perfCounters);
for (int i = 0; i < _storeCount; i++) {
_storeRefs[i] = new GCHandleRef<MemoryCacheStore> (new MemoryCacheStore(this, _perfCounters));
}
_stats = new MemoryCacheStatistics(this, config);
AppDomain appDomain = Thread.GetDomain();
@@ -321,8 +330,7 @@ namespace System.Runtime.Caching {
}
}
#endif
_storeMask = _storeCount - 1;
_stores = new MemoryCacheStore[_storeCount];
_storeRefs = new GCHandleRef<MemoryCacheStore>[_storeCount];
InitDisposableMembers(config);
}
@@ -392,10 +400,10 @@ namespace System.Runtime.Caching {
if (_stats != null) {
_stats.Dispose();
}
if (_stores != null) {
foreach (MemoryCacheStore store in _stores) {
if (store != null) {
store.Dispose();
if (_storeRefs != null) {
foreach (var storeRef in _storeRefs) {
if (storeRef != null) {
storeRef.Dispose();
}
}
}
@@ -442,8 +450,8 @@ namespace System.Runtime.Caching {
IEnumerator IEnumerable.GetEnumerator() {
Hashtable h = new Hashtable();
if (!IsDisposed) {
foreach (MemoryCacheStore store in _stores) {
store.CopyTo(h);
foreach (var storeRef in _storeRefs) {
storeRef.Target.CopyTo(h);
}
}
return h.GetEnumerator();
@@ -452,8 +460,8 @@ namespace System.Runtime.Caching {
protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator() {
Dictionary<string, object> h = new Dictionary<string, object>();
if (!IsDisposed) {
foreach (MemoryCacheStore store in _stores) {
store.CopyTo(h);
foreach (var storeRef in _storeRefs) {
storeRef.Target.CopyTo(h);
}
}
return h.GetEnumerator();
@@ -471,8 +479,8 @@ namespace System.Runtime.Caching {
}
long trimmed = 0;
if (_disposed == 0) {
foreach (MemoryCacheStore store in _stores) {
trimmed += store.TrimInternal(percent);
foreach (var storeRef in _storeRefs) {
trimmed += storeRef.Target.TrimInternal(percent);
}
}
return trimmed;
@@ -680,8 +688,8 @@ namespace System.Runtime.Caching {
}
long count = 0;
if (!IsDisposed) {
foreach (MemoryCacheStore store in _stores) {
count += store.Count;
foreach (var storeRef in _storeRefs) {
count += storeRef.Target.Count;
}
}
return count;

View File

@@ -27,7 +27,7 @@ namespace System.Runtime.Caching {
private int _lastTrimPercent;
private DateTime _lastTrimTime;
private int _pollingInterval;
private Timer _timer;
private GCHandleRef<Timer> _timerHandleRef;
private Object _timerLock;
private long _totalCountBeforeTrim;
@@ -44,16 +44,18 @@ namespace System.Runtime.Caching {
private void AdjustTimer() {
lock (_timerLock) {
if (_timer == null)
if (_timerHandleRef == null)
return;
Timer timer = _timerHandleRef.Target;
// the order of these if statements is important
// When above the high pressure mark, interval should be 5 seconds or less
if (_physicalMemoryMonitor.IsAboveHighPressure() || _cacheMemoryMonitor.IsAboveHighPressure()) {
if (_pollingInterval > MEMORYSTATUS_INTERVAL_5_SECONDS) {
_pollingInterval = MEMORYSTATUS_INTERVAL_5_SECONDS;
_timer.Change(_pollingInterval, _pollingInterval);
timer.Change(_pollingInterval, _pollingInterval);
}
return;
}
@@ -65,7 +67,7 @@ namespace System.Runtime.Caching {
int newPollingInterval = Math.Min(_configPollingInterval, MEMORYSTATUS_INTERVAL_30_SECONDS);
if (_pollingInterval != newPollingInterval) {
_pollingInterval = newPollingInterval;
_timer.Change(_pollingInterval, _pollingInterval);
timer.Change(_pollingInterval, _pollingInterval);
}
return;
}
@@ -73,7 +75,7 @@ namespace System.Runtime.Caching {
// there is no pressure, interval should be the value from config
if (_pollingInterval != _configPollingInterval) {
_pollingInterval = _configPollingInterval;
_timer.Change(_pollingInterval, _pollingInterval);
timer.Change(_pollingInterval, _pollingInterval);
}
}
}
@@ -126,7 +128,8 @@ namespace System.Runtime.Caching {
bool dispose = true;
try {
_cacheMemoryMonitor = new CacheMemoryMonitor(_memoryCache, _configCacheMemoryLimitMegabytes);
_timer = new Timer(new TimerCallback(CacheManagerTimerCallback), null, _configPollingInterval, _configPollingInterval);
Timer timer = new Timer(new TimerCallback(CacheManagerTimerCallback), null, _configPollingInterval, _configPollingInterval);
_timerHandleRef = new GCHandleRef<Timer>(timer);
dispose = false;
}
finally {
@@ -246,9 +249,9 @@ namespace System.Runtime.Caching {
public void Dispose() {
if (Interlocked.Exchange(ref _disposed, 1) == 0) {
lock (_timerLock) {
Timer timer = _timer;
if (timer != null && Interlocked.CompareExchange(ref _timer, null, timer) == timer) {
timer.Dispose();
GCHandleRef<Timer> timerHandleRef = _timerHandleRef;
if (timerHandleRef != null && Interlocked.CompareExchange(ref _timerHandleRef, null, timerHandleRef) == timerHandleRef) {
timerHandleRef.Dispose();
Dbg.Trace("MemoryCacheStats", "Stopped CacheMemoryTimers");
}
}

View File

@@ -7,24 +7,33 @@ using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace System.Runtime.Caching {
internal class SRef {
#if !MONO
private static Type s_type = Type.GetType("System.SizedReference", true, false);
private Object _sizedRef;
#endif
internal SRef(Object target) {
#if !MONO
_sizedRef = Activator.CreateInstance(s_type,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance,
null,
new object[] { target },
null);
#endif
}
internal long ApproximateSize {
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
get {
#if MONO
// TODO: .net uses System.SizedReference which contains approximate size after Gen 2 collection
return 16;
#else
object o = s_type.InvokeMember("ApproximateSize",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty,
null, // binder
@@ -32,6 +41,7 @@ namespace System.Runtime.Caching {
null, // args
CultureInfo.InvariantCulture);
return (long)o;
#endif
}
}
@@ -40,12 +50,87 @@ namespace System.Runtime.Caching {
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
internal void Dispose() {
#if !MONO
s_type.InvokeMember("Dispose",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null, // binder
_sizedRef, // target
null, // args
CultureInfo.InvariantCulture);
#endif
}
}
internal class SRefMultiple {
private SRef[] _srefs;
private long[] _sizes; // Getting SRef size in the debugger is extremely tedious so we keep the last read value here
internal SRefMultiple(object[] targets) {
_srefs = new SRef[targets.Length];
_sizes = new long[targets.Length];
for (int i = 0; i < targets.Length; i++) {
_srefs[i] = new SRef(targets[i]);
}
}
internal long ApproximateSize {
get {
long size = 0;
for (int i = 0; i < _srefs.Length; i++) {
size += (_sizes[i] = _srefs[i].ApproximateSize);
}
return size;
}
}
internal void Dispose() {
foreach (SRef s in _srefs) {
s.Dispose();
}
}
}
internal class GCHandleRef<T> : IDisposable
where T : class, IDisposable {
GCHandle _handle;
T _t;
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
public GCHandleRef(T t) {
_handle = GCHandle.Alloc(t);
}
public T Target {
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
get {
try {
T t = (T)_handle.Target;
if (t != null) {
return t;
}
}
catch (InvalidOperationException) {
// use the normal reference instead of throwing an exception when _handle is already freed
}
return _t;
}
}
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
public void Dispose() {
Target.Dispose();
// Safe to call Dispose more than once but not thread-safe
if (_handle.IsAllocated) {
// We must free the GC handle to avoid leaks.
// However after _handle is freed we no longer have access to its Target
// which will cause AVs and various race conditions under stress.
// We revert to using normal references after disposing the GC handle
_t = (T)_handle.Target;
_handle.Free();
}
}
}
}