diff --git a/Main/src/CodeJam.Main.csproj b/Main/src/CodeJam.Main.csproj
index c7d45e8e8..6df82dea2 100644
--- a/Main/src/CodeJam.Main.csproj
+++ b/Main/src/CodeJam.Main.csproj
@@ -278,6 +278,7 @@
+
diff --git a/Main/src/Collections/LazyDictionary.cs b/Main/src/Collections/LazyDictionary.cs
index 5826b1732..5f60fc642 100644
--- a/Main/src/Collections/LazyDictionary.cs
+++ b/Main/src/Collections/LazyDictionary.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Threading;
using CodeJam.Threading;
@@ -20,7 +21,10 @@ public static class LazyDictionary
/// Type of value
/// Function to create value on demand.
/// Key comparer.
- /// If true, creates a thread safe implementation
+ ///
+ /// If true, creates a thread safe implementation.
+ /// guaranteed to call only once.
+ ///
/// implementation.
[NotNull]
[Pure]
@@ -29,7 +33,7 @@ public static ILazyDictionary Create(
[NotNull] IEqualityComparer comparer,
bool threadSafe) =>
threadSafe
- ? new ConcurrentLazyDictionary(valueFactory, comparer)
+ ? new ExecSyncConcurrentLazyDictionary(valueFactory, comparer)
: (ILazyDictionary)new LazyDictionary(valueFactory, comparer);
///
@@ -38,7 +42,10 @@ public static ILazyDictionary Create(
/// Type of key
/// Type of value
/// Function to create value on demand.
- /// If true, creates a thread safe implementation
+ ///
+ /// If true, creates a thread safe implementation.
+ /// guaranteed to call only once.
+ ///
/// implementation.
[NotNull]
[Pure]
@@ -46,7 +53,63 @@ public static ILazyDictionary Create(
[NotNull] Func valueFactory,
bool threadSafe) =>
threadSafe
- ? new ConcurrentLazyDictionary(valueFactory)
+ ? new ExecSyncConcurrentLazyDictionary(valueFactory)
: (ILazyDictionary)new LazyDictionary(valueFactory);
+
+ ///
+ /// Creates implementation of .
+ ///
+ /// Type of key
+ /// Type of value
+ /// Function to create value on demand.
+ /// One of the enumeration values that specifies the thread safety mode.
+ /// implementation.
+ [NotNull]
+ [Pure]
+ public static ILazyDictionary Create(
+ [NotNull] Func valueFactory,
+ LazyThreadSafetyMode threadSafety)
+ {
+ switch (threadSafety)
+ {
+ case LazyThreadSafetyMode.None:
+ return new LazyDictionary(valueFactory);
+ case LazyThreadSafetyMode.PublicationOnly:
+ return new ConcurrentLazyDictionary(valueFactory);
+ case LazyThreadSafetyMode.ExecutionAndPublication:
+ return new ExecSyncConcurrentLazyDictionary(valueFactory);
+ default:
+ throw new ArgumentOutOfRangeException(nameof(threadSafety), threadSafety, null);
+ }
+ }
+
+ ///
+ /// Creates implementation of .
+ ///
+ /// Type of key
+ /// Type of value
+ /// Function to create value on demand.
+ /// Key comparer.
+ /// One of the enumeration values that specifies the thread safety mode.
+ /// implementation.
+ [NotNull]
+ [Pure]
+ public static ILazyDictionary Create(
+ [NotNull] Func valueFactory,
+ [NotNull] IEqualityComparer comparer,
+ LazyThreadSafetyMode threadSafety)
+ {
+ switch (threadSafety)
+ {
+ case LazyThreadSafetyMode.None:
+ return new LazyDictionary(valueFactory, comparer);
+ case LazyThreadSafetyMode.PublicationOnly:
+ return new ConcurrentLazyDictionary(valueFactory, comparer);
+ case LazyThreadSafetyMode.ExecutionAndPublication:
+ return new ExecSyncConcurrentLazyDictionary(valueFactory, comparer);
+ default:
+ throw new ArgumentOutOfRangeException(nameof(threadSafety), threadSafety, null);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Main/src/Threading/ExecSyncConcurrentLazyDictionary.cs b/Main/src/Threading/ExecSyncConcurrentLazyDictionary.cs
new file mode 100644
index 000000000..8ce6e248d
--- /dev/null
+++ b/Main/src/Threading/ExecSyncConcurrentLazyDictionary.cs
@@ -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
+{
+ ///
+ /// Dictionary with lazy values initialization.
+ ///
+ ///
+ /// Thread safe.
+ ///
+ [PublicAPI]
+ public class ExecSyncConcurrentLazyDictionary : ILazyDictionary
+ {
+ private readonly Func _valueFactory;
+ private readonly IEqualityComparer _comparer;
+ private readonly ConcurrentDictionary> _map;
+
+ ///
+ /// Initialize instance.
+ ///
+ /// Function to create value on demand.
+ /// Key comparer.
+ public ExecSyncConcurrentLazyDictionary([NotNull] Func valueFactory, IEqualityComparer comparer)
+ {
+ if (valueFactory == null) throw new ArgumentNullException(nameof(valueFactory));
+ _valueFactory = valueFactory;
+ _comparer = comparer;
+ _map = new ConcurrentDictionary>(comparer);
+ }
+
+ ///
+ /// Initialize instance.
+ ///
+ /// Function to create value on demand.
+ public ExecSyncConcurrentLazyDictionary([NotNull] Func valueFactory)
+ : this(valueFactory, EqualityComparer.Default)
+ { }
+
+ ///
+ /// Clears all created values
+ ///
+ public void Clear() => _map.Clear();
+
+ #region Implementation of IEnumerable
+ IEnumerator> IEnumerable>.GetEnumerator() =>
+ _map
+ .Select(v => new KeyValuePair(v.Key, v.Value.Value))
+ .GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable>)this).GetEnumerator();
+ #endregion
+
+ #region Implementation of IReadOnlyCollection>
+ int IReadOnlyCollection>.Count => _map.Count;
+ #endregion
+
+ #region Implementation of IReadOnlyDictionary
+ /// Determines whether the read-only dictionary contains an element that has the specified key.
+ /// true if the read-only dictionary contains an element that has the specified key; otherwise, false.
+ /// The key to locate.
+ ///
+ /// is null.
+ public bool ContainsKey(TKey key) => _map.ContainsKey(key);
+
+ /// Gets the value that is associated with the specified key.
+ /// true if the object that implements the interface contains an element that has the specified key; otherwise, false.
+ /// The key to locate.
+ /// 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 parameter. This parameter is passed uninitialized.
+ ///
+ /// is null.
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ Lazy lv;
+ var res = _map.TryGetValue(key, out lv);
+ value = res ? lv.Value : default(TValue);
+ return res;
+ }
+
+ /// Gets the element that has the specified key in the read-only dictionary.
+ /// The element that has the specified key in the read-only dictionary.
+ /// The key to locate.
+ ///
+ /// is null.
+ /// The property is retrieved and is not found.
+ public TValue this[TKey key] =>
+ _map
+ .GetOrAdd(
+ key,
+ new Lazy(() => _valueFactory(key), LazyThreadSafetyMode.ExecutionAndPublication))
+ .Value;
+
+ /// Gets an enumerable collection that contains the keys in the read-only dictionary.
+ /// An enumerable collection that contains the keys in the read-only dictionary.
+ public IEnumerable Keys => _map.Keys;
+
+ /// Gets an enumerable collection that contains the values in the read-only dictionary.
+ /// An enumerable collection that contains the values in the read-only dictionary.
+ public IEnumerable Values => _map.Values.Select(lv => lv.Value);
+ #endregion
+ }
+}
\ No newline at end of file