-
Notifications
You must be signed in to change notification settings - Fork 39
scopedec #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
scopedec #135
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
| using FluentAssertions; | ||
|
|
||
| namespace BitFaster.Caching.UnitTests | ||
| { | ||
| public class Disposable : IDisposable | ||
| { | ||
| public bool IsDisposed { get; set; } | ||
|
|
||
| public void Dispose() | ||
| { | ||
| this.IsDisposed.Should().BeFalse(); | ||
| IsDisposed = true; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace BitFaster.Caching.UnitTests | ||
| { | ||
| public class DisposableValueFactory | ||
| { | ||
| public Disposable Disposable { get; } = new Disposable(); | ||
|
|
||
| public Scoped<Disposable> Create(int key) | ||
| { | ||
| return new Scoped<Disposable>(this.Disposable); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,204 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
| using BitFaster.Caching.Lru; | ||
| using FluentAssertions; | ||
| using Xunit; | ||
|
|
||
| namespace BitFaster.Caching.UnitTests | ||
| { | ||
| public class ScopedCacheTests | ||
| { | ||
| private const int capacity = 6; | ||
| private readonly ScopedCache<int, Disposable> cache = new (new ConcurrentLru<int, Scoped<Disposable>>(capacity)); | ||
|
|
||
| [Fact] | ||
| public void WhenInnerCacheIsNullCtorThrows() | ||
| { | ||
| Action constructor = () => { var x = new ScopedCache<int, Disposable>(null); }; | ||
|
|
||
| constructor.Should().Throw<ArgumentNullException>(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenCreatedCapacityPropertyWrapsInnerCache() | ||
| { | ||
| this.cache.Capacity.Should().Be(capacity); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenItemIsAddedCountIsCorrect() | ||
| { | ||
| this.cache.Count.Should().Be(0); | ||
|
|
||
| this.cache.AddOrUpdate(1, new Disposable()); | ||
|
|
||
| this.cache.Count.Should().Be(1); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenKeyDoesNotExistAddOrUpdateAddsNewItem() | ||
| { | ||
| var d = new Disposable(); | ||
| this.cache.AddOrUpdate(1, d); | ||
|
|
||
| this.cache.ScopedTryGet(1, out var lifetime).Should().BeTrue(); | ||
| lifetime.Value.Should().Be(d); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenKeyExistsAddOrUpdateUpdatesExistingItem() | ||
| { | ||
| var d1 = new Disposable(); | ||
| var d2 = new Disposable(); | ||
| this.cache.AddOrUpdate(1, d1); | ||
| this.cache.AddOrUpdate(1, d2); | ||
|
|
||
| this.cache.ScopedTryGet(1, out var lifetime).Should().BeTrue(); | ||
| lifetime.Value.Should().Be(d2); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenItemUpdatedOldValueIsAliveUntilLifetimeCompletes() | ||
| { | ||
| var d1 = new Disposable(); | ||
| var d2 = new Disposable(); | ||
|
|
||
| // start a lifetime on 1 | ||
| this.cache.AddOrUpdate(1, d1); | ||
| this.cache.ScopedTryGet(1, out var lifetime1).Should().BeTrue(); | ||
|
|
||
| using (lifetime1) | ||
| { | ||
| // replace 1 | ||
| this.cache.AddOrUpdate(1, d2); | ||
|
|
||
| // cache reflects replacement | ||
| this.cache.ScopedTryGet(1, out var lifetime2).Should().BeTrue(); | ||
| lifetime2.Value.Should().Be(d2); | ||
|
|
||
| d1.IsDisposed.Should().BeFalse(); | ||
| } | ||
|
|
||
| d1.IsDisposed.Should().BeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenClearedItemsAreDisposed() | ||
| { | ||
| var d = new Disposable(); | ||
| this.cache.AddOrUpdate(1, d); | ||
|
|
||
| this.cache.Clear(); | ||
|
|
||
| d.IsDisposed.Should().BeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenItemExistsTryGetReturnsLifetime() | ||
| { | ||
| this.cache.AddOrUpdate(1, new Disposable()); | ||
| this.cache.ScopedTryGet(1, out var lifetime).Should().BeTrue(); | ||
|
|
||
| lifetime.Should().NotBeNull(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenItemDoesNotExistTryGetReturnsFalse() | ||
| { | ||
| this.cache.ScopedTryGet(1, out var lifetime).Should().BeFalse(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenScopeIsDisposedTryGetReturnsFalse() | ||
| { | ||
| var scope = new Scoped<Disposable>(new Disposable()); | ||
|
|
||
| this.cache.ScopedGetOrAdd(1, k => scope); | ||
|
|
||
| scope.Dispose(); | ||
|
|
||
| this.cache.ScopedTryGet(1, out var lifetime).Should().BeFalse(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenKeyDoesNotExistGetOrAddAddsValue() | ||
| { | ||
| this.cache.ScopedGetOrAdd(1, k => new Scoped<Disposable>(new Disposable())); | ||
|
|
||
| this.cache.ScopedTryGet(1, out var lifetime).Should().BeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task WhenKeyDoesNotExistGetOrAddAsyncAddsValue() | ||
| { | ||
| await this.cache.ScopedGetOrAddAsync(1, k => Task.FromResult(new Scoped<Disposable>(new Disposable()))); | ||
|
|
||
| this.cache.ScopedTryGet(1, out var lifetime).Should().BeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void GetOrAddDisposedScopeThrows() | ||
| { | ||
| var scope = new Scoped<Disposable>(new Disposable()); | ||
| scope.Dispose(); | ||
|
|
||
|
|
||
| Action getOrAdd = () => { this.cache.ScopedGetOrAdd(1, k => scope); }; | ||
|
|
||
| getOrAdd.Should().Throw<InvalidOperationException>(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void GetOrAddAsyncDisposedScopeThrows() | ||
| { | ||
| var scope = new Scoped<Disposable>(new Disposable()); | ||
| scope.Dispose(); | ||
|
|
||
| Func<Task> getOrAdd = async () => { await this.cache.ScopedGetOrAddAsync(1, k => Task.FromResult(scope)); }; | ||
|
|
||
| getOrAdd.Should().ThrowAsync<InvalidOperationException>(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenCacheContainsValuesTrim1RemovesColdestValue() | ||
| { | ||
| this.cache.AddOrUpdate(0, new Disposable()); | ||
| this.cache.AddOrUpdate(1, new Disposable()); | ||
| this.cache.AddOrUpdate(2, new Disposable()); | ||
|
|
||
| this.cache.Trim(1); | ||
|
|
||
| this.cache.ScopedTryGet(0, out var lifetime).Should().BeFalse(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenKeyDoesNotExistTryRemoveReturnsFalse() | ||
| { | ||
| this.cache.TryRemove(1).Should().BeFalse(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenKeyExistsTryRemoveReturnsTrue() | ||
| { | ||
| this.cache.AddOrUpdate(1, new Disposable()); | ||
| this.cache.TryRemove(1).Should().BeTrue(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenKeyDoesNotExistTryUpdateReturnsFalse() | ||
| { | ||
| this.cache.TryUpdate(1, new Disposable()).Should().BeFalse(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void WhenKeyExistsTryUpdateReturnsTrue() | ||
| { | ||
| this.cache.AddOrUpdate(1, new Disposable()); | ||
|
|
||
| this.cache.TryUpdate(1, new Disposable()).Should().BeTrue(); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace BitFaster.Caching | ||
| { | ||
| /// <summary> | ||
| /// Represents a generic cache of key/scoped IDisposable 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 IScopedCache<K, V> where V : IDisposable | ||
| { | ||
| /// <summary> | ||
| /// Gets the total number of items that can be stored in the cache. | ||
| /// </summary> | ||
| int Capacity { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the number of items currently held in the cache. | ||
| /// </summary> | ||
| int Count { get; } | ||
|
|
||
| /// <summary> | ||
| /// Attempts to create a lifetime for the value associated with the specified key from the cache | ||
| /// </summary> | ||
| /// <param name="key">The key of the value to get.</param> | ||
| /// <param name="lifetime">When this method returns, contains a lifetime for 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 ScopedTryGet(K key, out Lifetime<V> lifetime); | ||
|
|
||
| /// <summary> | ||
| /// Adds a key/scoped value pair to the cache if the key does not already exist. Returns a lifetime for either 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 scoped value for the key.</param> | ||
| /// <returns>The lifetime for the value associated with the key. The lifetime will be either reference 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 cache.</returns> | ||
| Lifetime<V> ScopedGetOrAdd(K key, Func<K, Scoped<V>> valueFactory); | ||
|
|
||
| /// <summary> | ||
| /// Adds a key/scoped value pair to the cache if the key does not already exist. Returns a lifetime for either 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 scoped value for the key.</param> | ||
| /// <returns>A task that represents the asynchronous ScopedGetOrAdd operation.</returns> | ||
| Task<Lifetime<V>> ScopedGetOrAddAsync(K key, Func<K, Task<Scoped<V>>> valueFactory); | ||
|
|
||
| /// <summary> | ||
| /// Attempts to remove the value that has the specified key. | ||
| /// </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); | ||
|
|
||
| /// <summary> | ||
| /// Attempts to update the value that has the specified key. | ||
| /// </summary> | ||
| /// <param name="key">The key of the element to update.</param> | ||
| /// <param name="value">The new value.</param> | ||
| /// <returns>true if the object was updated successfully; otherwise, false.</returns> | ||
| bool TryUpdate(K key, V value); | ||
|
|
||
| /// <summary> | ||
| /// Adds a key/value pair to the cache if the key does not already exist, or updates a key/value pair if the | ||
| /// key already exists. | ||
| /// </summary> | ||
| /// <param name="key">The key of the element to update.</param> | ||
| /// <param name="value">The new value.</param> | ||
| void AddOrUpdate(K key, V value); | ||
|
|
||
| /// <summary> | ||
| /// Removes all keys and values from the cache. | ||
| /// </summary> | ||
| void Clear(); | ||
|
|
||
| /// <summary> | ||
| /// Trim the specified number of items from the cache. | ||
| /// </summary> | ||
| /// <param name="itemCount">The number of items to remove.</param> | ||
| void Trim(int itemCount); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.