internal GCHandleContainer() { // Allocations of pinned gc handles done only once during the lifetime of a thread _thisPtrHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); _dynamicInvokeArgHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); _returnObjectHandle = UnsafeGCHandle.Alloc(null, GCHandleType.Pinned); }
private static Entry[] ResizeCacheForNewEntryAsNecessary() { Entry[] cache = s_cache; int entries = s_entries++; // If the cache has spare space, we are done if (2 * entries < cache.Length) { if (s_roundRobinFlushing) { cache[2 * entries] = null; cache[2 * entries + 1] = null; } return(cache); } // // Now, we have cache that is overflowing with results. We need to decide whether to resize it or start // flushing the old entries instead // // Start over counting the entries s_entries = 0; // See how long it has been since the last time the cache was overflowing long tickCount = InternalCalls.PalGetTickCount64(); int tickCountSinceLastOverflow = (int)(tickCount - s_tickCountOfLastOverflow); s_tickCountOfLastOverflow = tickCount; bool shrinkCache = false; bool growCache = false; if (cache.Length < DefaultCacheSize) { // If the cache have not reached the default size, just grow it without thinking about it much growCache = true; } else { if (tickCountSinceLastOverflow < cache.Length) { // We 'overflow' when 2*entries == cache.Length, so we have cache.Length / 2 entries that were // filled in tickCountSinceLastOverflow ms, which is 2ms/entry // If the fill rate of the cache is faster than ~2ms per entry, grow it if (cache.Length < MaximumCacheSize) { growCache = true; } } else if (tickCountSinceLastOverflow > cache.Length * 16) { // We 'overflow' when 2*entries == cache.Length, so we have ((cache.Length*16) / 2) entries that // were filled in tickCountSinceLastOverflow ms, which is 32ms/entry // If the fill rate of the cache is slower than 32ms per entry, shrink it if (cache.Length > DefaultCacheSize) { shrinkCache = true; } } // Otherwise, keep the current size and just keep flushing the entries round robin } Entry[] newCache = null; if (growCache || shrinkCache) { try { newCache = new Entry[shrinkCache ? (cache.Length / 2) : (cache.Length * 2)]; } catch (OutOfMemoryException) { // Failed to allocate a bigger/smaller cache. That is fine, keep the old one. } } if (newCache != null) { s_roundRobinFlushing = false; // Keep the reference to the old cache in a weak handle. We will try to use it to avoid hitting the // cache miss path until the GC collects it. if (s_previousCache.IsAllocated) { s_previousCache.Target = cache; } else { try { s_previousCache = UnsafeGCHandle.Alloc(cache, GCHandleType.Weak); } catch (OutOfMemoryException) { // Failed to allocate the handle to utilize the old cache, that is fine, we will just miss // out on repopulating the new cache from the old cache. s_previousCache = default(UnsafeGCHandle); } } return(s_cache = newCache); } else { s_roundRobinFlushing = true; return(cache); } }