Skip to content
Merged

docs #241

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
31 changes: 27 additions & 4 deletions BitFaster.Caching/Atomic/AsyncAtomicFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BitFaster.Caching.Atomic
{
/// <summary>
/// A class that provides simple, lightweight exactly once initialization for values
/// stored in a cache.
/// </summary>
/// <typeparam name="K">The type of the key.</typeparam>
/// <typeparam name="V">The type of the value.</typeparam>
[DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueIfCreated}")]
public sealed class AsyncAtomicFactory<K, V>
{
Expand All @@ -16,16 +19,30 @@ public sealed class AsyncAtomicFactory<K, V>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private V value;

/// <summary>
/// Initializes a new instance of the <see cref="AsyncAtomicFactory{K, V}"/> class.
/// </summary>
public AsyncAtomicFactory()
{
initializer = new Initializer();
}

/// <summary>
/// Initializes a new instance of the <see cref="AsyncAtomicFactory{K, V}"/> class with the
/// specified value.
/// </summary>
/// <param name="value">The value.</param>
public AsyncAtomicFactory(V value)
{
this.value = value;
}

/// <summary>
/// Gets the value. If <see cref="IsValueCreated"/> is false, calling <see cref="GetValueAsync"/> will force initialization via the <paramref name="valueFactory"/> parameter.
/// </summary>
/// <param name="key">The key associated with the value.</param>
/// <param name="valueFactory">The value factory to use to create the value when it is not initialized.</param>
/// <returns>The value.</returns>
public async ValueTask<V> GetValueAsync(K key, Func<K, Task<V>> valueFactory)
{
if (initializer == null)
Expand All @@ -36,8 +53,14 @@ public async ValueTask<V> GetValueAsync(K key, Func<K, Task<V>> valueFactory)
return await CreateValueAsync(key, valueFactory).ConfigureAwait(false);
}

/// <summary>
/// Gets a value indicating whether the value has been initialized.
/// </summary>
public bool IsValueCreated => initializer == null;

/// <summary>
/// Gets the value if it has been initialized, else default.
/// </summary>
public V ValueIfCreated
{
get
Expand Down Expand Up @@ -66,7 +89,7 @@ private async ValueTask<V> CreateValueAsync(K key, Func<K, Task<V>> valueFactory

private class Initializer
{
private object syncLock = new object();
private readonly object syncLock = new object();
private bool isInitialized;
private Task<V> valueTask;

Expand Down
32 changes: 27 additions & 5 deletions BitFaster.Caching/Atomic/AtomicFactory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BitFaster.Caching.Atomic
{
/// <summary>
/// A class that provides simple, lightweight exactly once initialization for values
/// stored in a cache.
/// </summary>
/// <typeparam name="K">The type of the key.</typeparam>
/// <typeparam name="V">The type of the value.</typeparam>
[DebuggerDisplay("IsValueCreated={IsValueCreated}, Value={ValueIfCreated}")]
public sealed class AtomicFactory<K, V>
{
Expand All @@ -16,16 +18,30 @@ public sealed class AtomicFactory<K, V>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private V value;

/// <summary>
/// Initializes a new instance of the <see cref="AtomicFactory{K, V}"/> class.
/// </summary>
public AtomicFactory()
{
initializer = new Initializer();
}

/// <summary>
/// Initializes a new instance of the <see cref="AtomicFactory{K, V}"/> class with the
/// specified value.
/// </summary>
/// <param name="value">The value.</param>
public AtomicFactory(V value)
{
this.value = value;
}

/// <summary>
/// Gets the value. If <see cref="IsValueCreated"/> is false, calling <see cref="GetValue"/> will force initialization via the <paramref name="valueFactory"/> parameter.
/// </summary>
/// <param name="key">The key associated with the value.</param>
/// <param name="valueFactory">The value factory to use to create the value when it is not initialized.</param>
/// <returns>The value.</returns>
public V GetValue(K key, Func<K, V> valueFactory)
{
if (initializer == null)
Expand All @@ -36,8 +52,14 @@ public V GetValue(K key, Func<K, V> valueFactory)
return CreateValue(key, valueFactory);
}

/// <summary>
/// Gets a value indicating whether the value has been initialized.
/// </summary>
public bool IsValueCreated => initializer == null;

/// <summary>
/// Gets the value if it has been initialized, else default.
/// </summary>
public V ValueIfCreated
{
get
Expand Down Expand Up @@ -66,7 +88,7 @@ private V CreateValue(K key, Func<K, V> valueFactory)

private class Initializer
{
private object syncLock = new object();
private readonly object syncLock = new object();
private bool isInitialized;
private V value;

Expand Down
5 changes: 5 additions & 0 deletions BitFaster.Caching/Atomic/AtomicFactoryAsyncCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

namespace BitFaster.Caching.Atomic
{
/// <summary>
/// A cache decorator for working with <see cref="AsyncAtomicFactory{K, V}"/> wrapped values, giving exactly once initialization.
/// </summary>
/// <typeparam name="K">The type of keys in the cache.</typeparam>
/// <typeparam name="V">The type of values in the cache.</typeparam>
[DebuggerDisplay("Count = {Count}")]
public sealed class AtomicFactoryAsyncCache<K, V> : IAsyncCache<K, V>
{
Expand Down
9 changes: 9 additions & 0 deletions BitFaster.Caching/Atomic/AtomicFactoryCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@

namespace BitFaster.Caching.Atomic
{
/// <summary>
/// A cache decorator for working with <see cref="AtomicFactory{K, V}"/> wrapped values, giving exactly once initialization.
/// </summary>
/// <typeparam name="K">The type of keys in the cache.</typeparam>
/// <typeparam name="V">The type of values in the cache.</typeparam>
[DebuggerDisplay("Count = {Count}")]
public sealed class AtomicFactoryCache<K, V> : ICache<K, V>
{
private readonly ICache<K, AtomicFactory<K, V>> cache;
private readonly Optional<ICacheEvents<K, V>> events;

/// <summary>
/// Initializes a new instance of the ScopedCache class with the specified inner cache.
/// </summary>
/// <param name="cache">The decorated cache.</param>
public AtomicFactoryCache(ICache<K, AtomicFactory<K, V>> cache)
{
if (cache == null)
Expand Down
5 changes: 5 additions & 0 deletions BitFaster.Caching/Atomic/AtomicFactoryScopedAsyncCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

namespace BitFaster.Caching.Atomic
{
/// <summary>
/// A cache decorator for working with <see cref="ScopedAsyncAtomicFactory{K, V}"/> wrapped values, giving exactly once initialization.
/// </summary>
/// <typeparam name="K">The type of keys in the cache.</typeparam>
/// <typeparam name="V">The type of values in the cache.</typeparam>
[DebuggerDisplay("Count = {Count}")]
public sealed class AtomicFactoryScopedAsyncCache<K, V> : IScopedAsyncCache<K, V> where V : IDisposable
{
Expand Down
5 changes: 5 additions & 0 deletions BitFaster.Caching/Atomic/AtomicFactoryScopedCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

namespace BitFaster.Caching.Atomic
{
/// <summary>
/// A cache decorator for working with <see cref="ScopedAtomicFactory{K, V}"/> wrapped values, giving exactly once initialization.
/// </summary>
/// <typeparam name="K">The type of keys in the cache.</typeparam>
/// <typeparam name="V">The type of values in the cache.</typeparam>
[DebuggerDisplay("Count = {Count}")]
public sealed class AtomicFactoryScopedCache<K, V> : IScopedCache<K, V> where V : IDisposable
{
Expand Down
37 changes: 36 additions & 1 deletion BitFaster.Caching/Atomic/ScopedAsyncAtomicFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,39 @@

namespace BitFaster.Caching.Atomic
{
/// <summary>
/// A class that provides simple, lightweight exactly once initialization for scoped values
/// stored in a cache.
/// </summary>
/// <typeparam name="K">The type of the key.</typeparam>
/// <typeparam name="V">The type of the value.</typeparam>
[DebuggerDisplay("IsValueCreated={initializer == null}, Value={ScopeIfCreated}")]
public sealed class ScopedAsyncAtomicFactory<K, V> : IScoped<V>, IDisposable where V : IDisposable
{
private Scoped<V> scope;
private Initializer initializer;

/// <summary>
/// Initializes a new instance of the <see cref="ScopedAsyncAtomicFactory{K, V}"/> class.
/// </summary>
public ScopedAsyncAtomicFactory()
{
initializer = new Initializer();
}

/// <summary>
/// Initializes a new instance of the <see cref="ScopedAsyncAtomicFactory{K, V}"/> class with the
/// specified value.
/// </summary>
/// <param name="value">The value.</param>
public ScopedAsyncAtomicFactory(V value)
{
scope = new Scoped<V>(value);
}

/// <summary>
/// Gets the scope if it has been initialized, else default.
/// </summary>
public Scoped<V> ScopeIfCreated
{
get
Expand All @@ -34,6 +51,12 @@ public Scoped<V> ScopeIfCreated
}
}

/// <summary>
/// Attempts to create a lifetime for the scoped value. The lifetime guarantees the value is alive until
/// the lifetime is disposed.
/// </summary>
/// <param name="lifetime">When this method returns, contains the Lifetime that was created, or the default value of the type if the operation failed.</param>
/// <returns>true if the Lifetime was created; otherwise false.</returns>
public bool TryCreateLifetime(out Lifetime<V> lifetime)
{
if (scope?.IsDisposed ?? false || initializer != null)
Expand All @@ -45,6 +68,13 @@ public bool TryCreateLifetime(out Lifetime<V> lifetime)
return scope.TryCreateLifetime(out lifetime);
}

/// <summary>
/// Attempts to create a lifetime for the scoped value. The lifetime guarantees the value is alive until
/// the lifetime is disposed.
/// </summary>
/// <param name="key">The key associated with the scoped value.</param>
/// <param name="valueFactory">The value factory to use to create the scoped value when it is not initialized.</param>
/// <returns>true if the Lifetime was created; otherwise false. If the lifetime was created, the new lifetime is also returned.</returns>
public async ValueTask<(bool success, Lifetime<V> lifetime)> TryCreateLifetimeAsync(K key, Func<K, Task<Scoped<V>>> valueFactory)
{
// if disposed, return
Expand Down Expand Up @@ -73,6 +103,11 @@ private async ValueTask InitializeScopeAsync(K key, Func<K, Task<Scoped<V>>> val
initializer = null;
}
}

/// <summary>
/// Terminates the scope and disposes the value. Once the scope is terminated, it is no longer
/// possible to create new lifetimes for the value.
/// </summary>
public void Dispose()
{
var init = initializer;
Expand All @@ -90,7 +125,7 @@ public void Dispose()

private class Initializer
{
private object syncLock = new object();
private readonly object syncLock = new object();
private bool isTaskInitialized;
private bool isTaskCompleted;
private bool isDisposeRequested;
Expand Down
Loading