| File: Collections\ConcurrentCache.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Microsoft.CodeAnalysis { // very simple cache with a specified size. // expiration policy is "new entry wins over old entry if hashed into the same bucket" internal sealed class ConcurrentCache<TKey, TValue> : CachingBase<ConcurrentCache<TKey, TValue>.Entry> where TKey : notnull { private readonly IEqualityComparer<TKey> _keyComparer; // class, to ensure atomic updates. internal class Entry { internal readonly int hash; internal readonly TKey key; internal readonly TValue value; internal Entry(int hash, TKey key, TValue value) { this.hash = hash; this.key = key; this.value = value; } } public ConcurrentCache(int size, IEqualityComparer<TKey> keyComparer) // Defer creating the backing array until it is actually needed. This saves on expensive allocations for // short-lived compilations that do not end up using the cache. As the cache is simple best-effort, it's // fine if multiple threads end up creating the backing array at the same time. One thread will be last and // will win, and the others will just end up creating a small piece of garbage that will be collected. : base(size, createBackingArray: false) { _keyComparer = keyComparer; } public ConcurrentCache(int size) : this(size, EqualityComparer<TKey>.Default) { } public bool TryAdd(TKey key, TValue value) { var hash = _keyComparer.GetHashCode(key); var idx = hash & mask; var entry = this.Entries[idx]; if (entry != null && entry.hash == hash && _keyComparer.Equals(entry.key, key)) { return false; } Entries[idx] = new Entry(hash, key, value); return true; } public bool TryGetValue(TKey key, [MaybeNullWhen(returnValue: false)] out TValue value) { int hash = _keyComparer.GetHashCode(key); int idx = hash & mask; var entry = this.Entries[idx]; if (entry != null && entry.hash == hash && _keyComparer.Equals(entry.key, key)) { value = entry.value; return true; } value = default; return false; } } }