Skip to content

Commit

Permalink
Add execution synchronized LazyDictionary implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewvk committed Aug 20, 2016
1 parent 75cb90d commit 44ce93f
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 4 deletions.
1 change: 1 addition & 0 deletions Main/src/CodeJam.Main.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@
</Compile>
<Compile Include="Threading\AsyncOperationHelper.cs" />
<Compile Include="Threading\ConcurrentLazyDictionary.cs" />
<Compile Include="Threading\ExecSyncConcurrentLazyDictionary.cs" />
<Compile Include="DisposableExtensions.cs" />
<Compile Include="Collections\EnumerableExtensions.TopoSort.cs" />
<Compile Include="Disposable.cs" />
Expand Down
71 changes: 67 additions & 4 deletions Main/src/Collections/LazyDictionary.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading;

using CodeJam.Threading;

Expand All @@ -20,7 +21,10 @@ public static class LazyDictionary
/// <typeparam name="TValue">Type of value</typeparam>
/// <param name="valueFactory">Function to create value on demand.</param>
/// <param name="comparer">Key comparer.</param>
/// <param name="threadSafe">If true, creates a thread safe implementation</param>
/// <param name="threadSafe">
/// If true, creates a thread safe implementation.
/// <paramref name="valueFactory"/> guaranteed to call only once.
/// </param>
/// <returns><see cref="ILazyDictionary{TKey,TValue}"/> implementation.</returns>
[NotNull]
[Pure]
Expand All @@ -29,7 +33,7 @@ public static ILazyDictionary<TKey, TValue> Create<TKey, TValue>(
[NotNull] IEqualityComparer<TKey> comparer,
bool threadSafe) =>
threadSafe
? new ConcurrentLazyDictionary<TKey, TValue>(valueFactory, comparer)
? new ExecSyncConcurrentLazyDictionary<TKey, TValue>(valueFactory, comparer)
: (ILazyDictionary<TKey, TValue>)new LazyDictionary<TKey, TValue>(valueFactory, comparer);

/// <summary>
Expand All @@ -38,15 +42,74 @@ public static ILazyDictionary<TKey, TValue> Create<TKey, TValue>(
/// <typeparam name="TKey">Type of key</typeparam>
/// <typeparam name="TValue">Type of value</typeparam>
/// <param name="valueFactory">Function to create value on demand.</param>
/// <param name="threadSafe">If true, creates a thread safe implementation</param>
/// <param name="threadSafe">
/// If true, creates a thread safe implementation.
/// <paramref name="valueFactory"/> guaranteed to call only once.
/// </param>
/// <returns><see cref="ILazyDictionary{TKey,TValue}"/> implementation.</returns>
[NotNull]
[Pure]
public static ILazyDictionary<TKey, TValue> Create<TKey, TValue>(
[NotNull] Func<TKey, TValue> valueFactory,
bool threadSafe) =>
threadSafe
? new ConcurrentLazyDictionary<TKey, TValue>(valueFactory)
? new ExecSyncConcurrentLazyDictionary<TKey, TValue>(valueFactory)
: (ILazyDictionary<TKey, TValue>)new LazyDictionary<TKey, TValue>(valueFactory);

/// <summary>
/// Creates implementation of <see cref="ILazyDictionary{TKey,TValue}"/>.
/// </summary>
/// <typeparam name="TKey">Type of key</typeparam>
/// <typeparam name="TValue">Type of value</typeparam>
/// <param name="valueFactory">Function to create value on demand.</param>
/// <param name="threadSafety">One of the enumeration values that specifies the thread safety mode. </param>
/// <returns><see cref="ILazyDictionary{TKey,TValue}"/> implementation.</returns>
[NotNull]
[Pure]
public static ILazyDictionary<TKey, TValue> Create<TKey, TValue>(
[NotNull] Func<TKey, TValue> valueFactory,
LazyThreadSafetyMode threadSafety)
{
switch (threadSafety)
{
case LazyThreadSafetyMode.None:
return new LazyDictionary<TKey, TValue>(valueFactory);
case LazyThreadSafetyMode.PublicationOnly:
return new ConcurrentLazyDictionary<TKey, TValue>(valueFactory);
case LazyThreadSafetyMode.ExecutionAndPublication:
return new ExecSyncConcurrentLazyDictionary<TKey, TValue>(valueFactory);
default:
throw new ArgumentOutOfRangeException(nameof(threadSafety), threadSafety, null);
}
}

/// <summary>
/// Creates implementation of <see cref="ILazyDictionary{TKey,TValue}"/>.
/// </summary>
/// <typeparam name="TKey">Type of key</typeparam>
/// <typeparam name="TValue">Type of value</typeparam>
/// <param name="valueFactory">Function to create value on demand.</param>
/// <param name="comparer">Key comparer.</param>
/// <param name="threadSafety">One of the enumeration values that specifies the thread safety mode. </param>
/// <returns><see cref="ILazyDictionary{TKey,TValue}"/> implementation.</returns>
[NotNull]
[Pure]
public static ILazyDictionary<TKey, TValue> Create<TKey, TValue>(
[NotNull] Func<TKey, TValue> valueFactory,
[NotNull] IEqualityComparer<TKey> comparer,
LazyThreadSafetyMode threadSafety)
{
switch (threadSafety)
{
case LazyThreadSafetyMode.None:
return new LazyDictionary<TKey, TValue>(valueFactory, comparer);
case LazyThreadSafetyMode.PublicationOnly:
return new ConcurrentLazyDictionary<TKey, TValue>(valueFactory, comparer);
case LazyThreadSafetyMode.ExecutionAndPublication:
return new ExecSyncConcurrentLazyDictionary<TKey, TValue>(valueFactory, comparer);
default:
throw new ArgumentOutOfRangeException(nameof(threadSafety), threadSafety, null);
}
}
}
}
110 changes: 110 additions & 0 deletions Main/src/Threading/ExecSyncConcurrentLazyDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

using CodeJam.Collections;

using JetBrains.Annotations;

namespace CodeJam.Threading
{
/// <summary>
/// Dictionary with lazy values initialization.
/// </summary>
/// <remarks>
/// Thread safe.
/// </remarks>
[PublicAPI]
public class ExecSyncConcurrentLazyDictionary<TKey, TValue> : ILazyDictionary<TKey, TValue>
{
private readonly Func<TKey, TValue> _valueFactory;
private readonly IEqualityComparer<TKey> _comparer;
private readonly ConcurrentDictionary<TKey, Lazy<TValue>> _map;

/// <summary>
/// Initialize instance.
/// </summary>
/// <param name="valueFactory">Function to create value on demand.</param>
/// <param name="comparer">Key comparer.</param>
public ExecSyncConcurrentLazyDictionary([NotNull] Func<TKey, TValue> valueFactory, IEqualityComparer<TKey> comparer)
{
if (valueFactory == null) throw new ArgumentNullException(nameof(valueFactory));
_valueFactory = valueFactory;
_comparer = comparer;
_map = new ConcurrentDictionary<TKey, Lazy<TValue>>(comparer);
}

/// <summary>
/// Initialize instance.
/// </summary>
/// <param name="valueFactory">Function to create value on demand.</param>
public ExecSyncConcurrentLazyDictionary([NotNull] Func<TKey, TValue> valueFactory)
: this(valueFactory, EqualityComparer<TKey>.Default)
{ }

/// <summary>
/// Clears all created values
/// </summary>
public void Clear() => _map.Clear();

#region Implementation of IEnumerable
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() =>
_map
.Select(v => new KeyValuePair<TKey, TValue>(v.Key, v.Value.Value))
.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator();
#endregion

#region Implementation of IReadOnlyCollection<out KeyValuePair<TKey,TValue>>
int IReadOnlyCollection<KeyValuePair<TKey, TValue>>.Count => _map.Count;
#endregion

#region Implementation of IReadOnlyDictionary<TKey,TValue>
/// <summary>Determines whether the read-only dictionary contains an element that has the specified key.</summary>
/// <returns>true if the read-only dictionary contains an element that has the specified key; otherwise, false.</returns>
/// <param name="key">The key to locate.</param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="key" /> is null.</exception>
public bool ContainsKey(TKey key) => _map.ContainsKey(key);

/// <summary>Gets the value that is associated with the specified key.</summary>
/// <returns>true if the object that implements the <see cref="T:System.Collections.Generic.IReadOnlyDictionary`2" /> interface contains an element that has the specified key; otherwise, false.</returns>
/// <param name="key">The key to locate.</param>
/// <param name="value">When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the <paramref name="value" /> parameter. This parameter is passed uninitialized.</param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="key" /> is null.</exception>
public bool TryGetValue(TKey key, out TValue value)
{
Lazy<TValue> lv;
var res = _map.TryGetValue(key, out lv);
value = res ? lv.Value : default(TValue);
return res;
}

/// <summary>Gets the element that has the specified key in the read-only dictionary.</summary>
/// <returns>The element that has the specified key in the read-only dictionary.</returns>
/// <param name="key">The key to locate.</param>
/// <exception cref="T:System.ArgumentNullException">
/// <paramref name="key" /> is null.</exception>
/// <exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and <paramref name="key" /> is not found. </exception>
public TValue this[TKey key] =>
_map
.GetOrAdd(
key,
new Lazy<TValue>(() => _valueFactory(key), LazyThreadSafetyMode.ExecutionAndPublication))
.Value;

/// <summary>Gets an enumerable collection that contains the keys in the read-only dictionary. </summary>
/// <returns>An enumerable collection that contains the keys in the read-only dictionary.</returns>
public IEnumerable<TKey> Keys => _map.Keys;

/// <summary>Gets an enumerable collection that contains the values in the read-only dictionary.</summary>
/// <returns>An enumerable collection that contains the values in the read-only dictionary.</returns>
public IEnumerable<TValue> Values => _map.Values.Select(lv => lv.Value);
#endregion
}
}

0 comments on commit 44ce93f

Please sign in to comment.