| File: Microsoft\CSharp\RuntimeBinder\BinderEquivalence.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.CSharp\src\Microsoft.CSharp.csproj (Microsoft.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Threading; namespace Microsoft.CSharp.RuntimeBinder { internal static class BinderEquivalence { // An upper bound on the size of binder cache. // We do not need to cache all the binders, 4K should be enough for most cases. // // For the perspective: the dynamic testsuite has a lot of dynamic operations, // but ends up needing only ~500 binders once caching is enabled. // // NOTE: // typical C# binders, once created, are rooted to their callsites, which are stored in static fields. // the cache is unlikely to extend the life time of the binders. // the limit here is just to have assurance on how large the cache may get. private const uint BINDER_CACHE_LIMIT = 4096; // keep a separate count because it is cheaper than calling CD.Count() // it does not need to be very precise either private static int cachedBinderCount; // it is unlikely to see a lot of contention on the binder cache. // creating binders is not a very frequent operation. // typically a dynamic operation in the source will create just one binder lazily when first executed. private static readonly ConcurrentDictionary<ICSharpBinder, ICSharpBinder> binderEquivalenceCache = new ConcurrentDictionary<ICSharpBinder, ICSharpBinder>(concurrencyLevel: 2, capacity: 32, new BinderEqualityComparer()); internal static T TryGetExisting<T>(this T binder) where T : ICSharpBinder { var fromCache = binderEquivalenceCache.GetOrAdd(binder, binder); if (fromCache == (object)binder) { var count = Interlocked.Increment(ref cachedBinderCount); // a simple eviction policy - // if cache grows too big, just flush it and start over. if ((uint)count > BINDER_CACHE_LIMIT) { binderEquivalenceCache.Clear(); cachedBinderCount = 0; } } return (T)fromCache; } internal sealed class BinderEqualityComparer : IEqualityComparer<ICSharpBinder> { public bool Equals(ICSharpBinder x, ICSharpBinder y) => x.IsEquivalentTo(y); public int GetHashCode(ICSharpBinder obj) => obj.GetGetBinderEquivalenceHash(); } } }