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