Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public ConcurrentTLruTests()
[Fact]
public void ConstructAddAndRetrieveWithDefaultCtorReturnsValue()
{
var x = new ConcurrentTLru<int, int>(3);
var x = new ConcurrentTLru<int, int>(3, TimeSpan.FromSeconds(1));

x.GetOrAdd(1, k => k).Should().Be(1);
}
Expand Down
2 changes: 1 addition & 1 deletion BitFaster.Caching.UnitTests/Lru/FastConcurrentTLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void ConstructAddAndRetrieveWithCustomComparerReturnsValue()
[Fact]
public void ConstructAddAndRetrieveWithDefaultCtorReturnsValue()
{
var x = new FastConcurrentTLru<int, int>(3);
var x = new FastConcurrentTLru<int, int>(3, TimeSpan.FromSeconds(1));

x.GetOrAdd(1, k => k).Should().Be(1);
}
Expand Down
31 changes: 31 additions & 0 deletions BitFaster.Caching/ICache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,45 @@

namespace BitFaster.Caching
{
/// <summary>
/// Represents a generic cache of key/value pairs.
/// </summary>
/// <typeparam name="K">The type of keys in the cache.</typeparam>
/// <typeparam name="V">The type of values in the cache.</typeparam>
public interface ICache<K, V>
{
/// <summary>
/// Attempts to get the value associated with the specified key from the cache.
/// </summary>
/// <param name="key">The key of the value to get.</param>
/// <param name="value">When this method returns, contains the object from the cache that has the specified key, or the default value of the type if the operation failed.</param>
/// <returns>true if the key was found in the cache; otherwise, false.</returns>
bool TryGet(K key, out V value);

/// <summary>
/// Adds a key/value pair to the cache if the key does not already exist. Returns the new value, or the
/// existing value if the key already exists.
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="valueFactory">The factory function used to generate a value for the key.</param>
/// <returns>The value for the key. This will be either the existing value for the key if the key is already
/// in the cache, or the new value if the key was not in the dictionary.</returns>
V GetOrAdd(K key, Func<K, V> valueFactory);

/// <summary>
/// Adds a key/value pair to the cache if the key does not already exist. Returns the new value, or the
/// existing value if the key already exists.
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="valueFactory">The factory function used to asynchronously generate a value for the key.</param>
/// <returns>A task that represents the asynchronous GetOrAdd operation.</returns>
Task<V> GetOrAddAsync(K key, Func<K, Task<V>> valueFactory);

/// <summary>
/// Attempts to remove and return the value that has the specified key from the cache.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
/// <returns>true if the object was removed successfully; otherwise, false.</returns>
bool TryRemove(K key);
}
}
16 changes: 16 additions & 0 deletions BitFaster.Caching/Lifetime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,35 @@

namespace BitFaster.Caching
{
/// <summary>
/// Represents the lifetime of a value. The value is alive and valid for use until the
/// lifetime is disposed.
/// </summary>
/// <typeparam name="T">The type of value</typeparam>
public class Lifetime<T> : IDisposable
{
private readonly Action onDisposeAction;
private bool isDisposed;

/// <summary>
/// Initializes a new instance of the Lifetime class.
/// </summary>
/// <param name="value">The value to keep alive.</param>
/// <param name="onDisposeAction">The action to perform when the lifetime is terminated.</param>
public Lifetime(T value, Action onDisposeAction)
{
this.Value = value;
this.onDisposeAction = onDisposeAction;
}

/// <summary>
/// Gets the value.
/// </summary>
public T Value { get; }

/// <summary>
/// Terminates the lifetime and performs any cleanup required to release the value.
/// </summary>
public void Dispose()
{
if (!this.isDisposed)
Expand Down
4 changes: 4 additions & 0 deletions BitFaster.Caching/Lru/ClassicLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public ClassicLru(int concurrencyLevel, int capacity, IEqualityComparer<K> compa

public double HitRatio => (double)requestHitCount / (double)requestTotalCount;

///<inheritdoc/>
public bool TryGet(K key, out V value)
{
Interlocked.Increment(ref requestTotalCount);
Expand All @@ -68,6 +69,7 @@ public bool TryGet(K key, out V value)
return false;
}

///<inheritdoc/>
public V GetOrAdd(K key, Func<K, V> valueFactory)
{
if (this.TryGet(key, out var value))
Expand Down Expand Up @@ -114,6 +116,7 @@ public V GetOrAdd(K key, Func<K, V> valueFactory)
return this.GetOrAdd(key, valueFactory);
}

///<inheritdoc/>
public async Task<V> GetOrAddAsync(K key, Func<K, Task<V>> valueFactory)
{
if (this.TryGet(key, out var value))
Expand Down Expand Up @@ -160,6 +163,7 @@ public async Task<V> GetOrAddAsync(K key, Func<K, Task<V>> valueFactory)
return await this.GetOrAddAsync(key, valueFactory);
}

///<inheritdoc/>
public bool TryRemove(K key)
{
if (dictionary.TryRemove(key, out var node))
Expand Down
16 changes: 16 additions & 0 deletions BitFaster.Caching/Lru/ConcurrentLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,34 @@

namespace BitFaster.Caching.Lru
{
///<inheritdoc/>
public sealed class ConcurrentLru<K, V> : TemplateConcurrentLru<K, V, LruItem<K, V>, LruPolicy<K, V>, HitCounter>
{
/// <summary>
/// Initializes a new instance of the ConcurrentLru class with the specified capacity that has the default
/// concurrency level, and uses the default comparer for the key type.
/// </summary>
/// <param name="capacity">The maximum number of elements that the ConcurrentLru can contain.</param>
public ConcurrentLru(int capacity)
: base(Defaults.ConcurrencyLevel, capacity, EqualityComparer<K>.Default, new LruPolicy<K, V>(), new HitCounter())
{
}

/// <summary>
/// Initializes a new instance of the ConcurrentLru class that has the specified concurrency level, has the
/// specified initial capacity, and uses the specified IEqualityComparer<T>.
/// </summary>
/// <param name="concurrencyLevel">The estimated number of threads that will update the ConcurrentLru concurrently.</param>
/// <param name="capacity">The maximum number of elements that the ConcurrentLru can contain.</param>
/// <param name="comparer">The IEqualityComparer<T> implementation to use when comparing keys.</param>
public ConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer<K> comparer)
: base(concurrencyLevel, capacity, comparer, new LruPolicy<K, V>(), new HitCounter())
{
}

/// <summary>
/// Gets the ratio of hits to misses, where a value of 1 indicates 100% hits.
/// </summary>
public double HitRatio => this.hitCounter.HitRatio;
}
}
22 changes: 20 additions & 2 deletions BitFaster.Caching/Lru/ConcurrentTLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,36 @@

namespace BitFaster.Caching.Lru
{
///<inheritdoc/>
public sealed class ConcurrentTLru<K, V> : TemplateConcurrentLru<K, V, LongTickCountLruItem<K, V>, TLruLongTicksPolicy<K, V>, HitCounter>
{
public ConcurrentTLru(int capacity)
: base(Defaults.ConcurrencyLevel, capacity, EqualityComparer<K>.Default, new TLruLongTicksPolicy<K, V>(), new HitCounter())
/// <summary>
/// Initializes a new instance of the ConcurrentTLru class with the specified capacity and time to live that has the default
/// concurrency level, and uses the default comparer for the key type.
/// </summary>
/// <param name="capacity">The maximum number of elements that the ConcurrentTLru can contain.</param>
/// <param name="timeToLive">The time to live for cached values.</param>
public ConcurrentTLru(int capacity, TimeSpan timeToLive)
: base(Defaults.ConcurrencyLevel, capacity, EqualityComparer<K>.Default, new TLruLongTicksPolicy<K, V>(timeToLive), new HitCounter())
{
}

/// <summary>
/// Initializes a new instance of the ConcurrentTLru class that has the specified concurrency level, has the
/// specified initial capacity, uses the specified IEqualityComparer<T>, and has the specified time to live.
/// </summary>
/// <param name="concurrencyLevel">The estimated number of threads that will update the ConcurrentTLru concurrently.</param>
/// <param name="capacity">The maximum number of elements that the ConcurrentTLru can contain.</param>
/// <param name="comparer">The IEqualityComparer<T> implementation to use when comparing keys.</param>
/// <param name="timeToLive">The time to live for cached values.</param>
public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer<K> comparer, TimeSpan timeToLive)
: base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy<K, V>(timeToLive), new HitCounter())
{
}

/// <summary>
/// Gets the ratio of hits to misses, where a value of 1 indicates 100% hits.
/// </summary>
public double HitRatio => this.hitCounter.HitRatio;
}
}
13 changes: 13 additions & 0 deletions BitFaster.Caching/Lru/FastConcurrentLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@

namespace BitFaster.Caching.Lru
{
///<inheritdoc/>
public sealed class FastConcurrentLru<K, V> : TemplateConcurrentLru<K, V, LruItem<K, V>, LruPolicy<K, V>, NullHitCounter>
{
/// <summary>
/// Initializes a new instance of the FastConcurrentLru class with the specified capacity that has the default
/// concurrency level, and uses the default comparer for the key type.
/// </summary>
/// <param name="capacity">The maximum number of elements that the FastConcurrentLru can contain.</param>
public FastConcurrentLru(int capacity)
: base(Defaults.ConcurrencyLevel, capacity, EqualityComparer<K>.Default, new LruPolicy<K, V>(), new NullHitCounter())
{
}

/// <summary>
/// Initializes a new instance of the FastConcurrentLru class that has the specified concurrency level, has the
/// specified initial capacity, and uses the specified IEqualityComparer<T>.
/// </summary>
/// <param name="concurrencyLevel">The estimated number of threads that will update the FastConcurrentLru concurrently.</param>
/// <param name="capacity">The maximum number of elements that the FastConcurrentLru can contain.</param>
/// <param name="comparer">The IEqualityComparer<T> implementation to use when comparing keys.</param>
public FastConcurrentLru(int concurrencyLevel, int capacity, IEqualityComparer<K> comparer)
: base(concurrencyLevel, capacity, comparer, new LruPolicy<K, V>(), new NullHitCounter())
{
Expand Down
19 changes: 17 additions & 2 deletions BitFaster.Caching/Lru/FastConcurrentTLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,28 @@

namespace BitFaster.Caching.Lru
{
///<inheritdoc/>
public sealed class FastConcurrentTLru<K, V> : TemplateConcurrentLru<K, V, LongTickCountLruItem<K, V>, TLruLongTicksPolicy<K, V>, NullHitCounter>
{
public FastConcurrentTLru(int capacity)
: base(Defaults.ConcurrencyLevel, capacity, EqualityComparer<K>.Default, new TLruLongTicksPolicy<K, V>(), new NullHitCounter())
/// <summary>
/// Initializes a new instance of the FastConcurrentTLru class with the specified capacity and time to live that has the default
/// concurrency level, and uses the default comparer for the key type.
/// </summary>
/// <param name="capacity">The maximum number of elements that the FastConcurrentTLru can contain.</param>
/// <param name="timeToLive">The time to live for cached values.</param>
public FastConcurrentTLru(int capacity, TimeSpan timeToLive)
: base(Defaults.ConcurrencyLevel, capacity, EqualityComparer<K>.Default, new TLruLongTicksPolicy<K, V>(timeToLive), new NullHitCounter())
{
}

/// <summary>
/// Initializes a new instance of the FastConcurrentTLru class that has the specified concurrency level, has the
/// specified initial capacity, uses the specified IEqualityComparer<T>, and has the specified time to live.
/// </summary>
/// <param name="concurrencyLevel">The estimated number of threads that will update the FastConcurrentTLru concurrently.</param>
/// <param name="capacity">The maximum number of elements that the FastConcurrentTLru can contain.</param>
/// <param name="comparer">The IEqualityComparer<T> implementation to use when comparing keys.</param>
/// <param name="timeToLive">The time to live for cached values.</param>
public FastConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer<K> comparer, TimeSpan timeToLive)
: base(concurrencyLevel, capacity, comparer, new TLruLongTicksPolicy<K, V>(timeToLive), new NullHitCounter())
{
Expand Down
4 changes: 4 additions & 0 deletions BitFaster.Caching/Lru/TemplateConcurrentLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public TemplateConcurrentLru(

public int ColdCount => this.coldCount;

///<inheritdoc/>
public bool TryGet(K key, out V value)
{
I item;
Expand All @@ -115,6 +116,7 @@ public bool TryGet(K key, out V value)
return false;
}

///<inheritdoc/>
public V GetOrAdd(K key, Func<K, V> valueFactory)
{
if (this.TryGet(key, out var value))
Expand All @@ -137,6 +139,7 @@ public V GetOrAdd(K key, Func<K, V> valueFactory)
return this.GetOrAdd(key, valueFactory);
}

///<inheritdoc/>
public async Task<V> GetOrAddAsync(K key, Func<K, Task<V>> valueFactory)
{
if (this.TryGet(key, out var value))
Expand All @@ -159,6 +162,7 @@ public async Task<V> GetOrAddAsync(K key, Func<K, Task<V>> valueFactory)
return await this.GetOrAddAsync(key, valueFactory).ConfigureAwait(false);
}

///<inheritdoc/>
public bool TryRemove(K key)
{
// Possible race condition:
Expand Down
17 changes: 17 additions & 0 deletions BitFaster.Caching/ReferenceCount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ private ReferenceCount(TValue value, int referenceCount)
this.count = referenceCount;
}

/// <summary>
/// Gets the value.
/// </summary>
public TValue Value
{
get
Expand All @@ -33,6 +36,9 @@ public TValue Value
}
}

/// <summary>
/// Gets the count.
/// </summary>
public int Count
{
get
Expand All @@ -41,6 +47,10 @@ public int Count
}
}

/// <summary>
/// Create a copy of the ReferenceCount with the count incremented by 1.
/// </summary>
/// <returns>A copy of the ReferenceCount with the count incremented by 1.</returns>
public ReferenceCount<TValue> IncrementCopy()
{
if (this.count <= 0 && this.value is IDisposable)
Expand All @@ -51,23 +61,30 @@ public ReferenceCount<TValue> IncrementCopy()
return new ReferenceCount<TValue>(this.value, this.count + 1);
}

/// <summary>
/// Create a copy of the ReferenceCount with the count decremented by 1.
/// </summary>
/// <returns>A copy of the ReferenceCount with the count decremented by 1.</returns>
public ReferenceCount<TValue> DecrementCopy()
{
return new ReferenceCount<TValue>(this.value, this.count - 1);
}

///<inheritdoc/>
public override bool Equals(object obj)
{
return Equals(obj as ReferenceCount<TValue>);
}

///<inheritdoc/>
public bool Equals(ReferenceCount<TValue> other)
{
return other != null &&
EqualityComparer<TValue>.Default.Equals(value, other.value) &&
count == other.count;
}

///<inheritdoc/>
public override int GetHashCode()
{
var hashCode = -1491496004;
Expand Down
Loading