All files
[pithos-ms-client] / trunk / Libraries / ParallelExtensionsExtras / CoordinationDataStructures / AsyncCoordination / AsyncCache.cs
1 //--------------------------------------------------------------------------
2 // 
3 //  Copyright (c) Microsoft Corporation.  All rights reserved. 
4 // 
5 //  File: AsyncCache.cs
6 //
7 //--------------------------------------------------------------------------
8
9 using System.Collections;
10 using System.Collections.Concurrent;
11 using System.Collections.Generic;
12 using System.Diagnostics;
13 using System.Linq;
14 using System.Net;
15 using System.Threading.Tasks;
16
17 namespace System.Threading
18 {
19     /// <summary>Debugger type proxy for AsyncCache.</summary>
20     /// <typeparam name="TKey">Specifies the type of the cache's keys.</typeparam>
21     /// <typeparam name="TValue">Specifies the type of the cache's values.</typeparam>
22     internal class AsyncCache_DebugView<TKey, TValue>
23     {
24         private readonly AsyncCache<TKey, TValue> _asyncCache;
25
26         internal AsyncCache_DebugView(AsyncCache<TKey,TValue> asyncCache)
27         {
28             _asyncCache = asyncCache;
29         }
30
31         [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
32         internal KeyValuePair<TKey, Task<TValue>>[] Values
33         {
34             get { return _asyncCache.ToArray(); }
35         }
36     }
37
38     /// <summary>Caches asynchronously retrieved data.</summary>
39     /// <typeparam name="TKey">Specifies the type of the cache's keys.</typeparam>
40     /// <typeparam name="TValue">Specifies the type of the cache's values.</typeparam>
41     [DebuggerTypeProxy(typeof(AsyncCache_DebugView<,>))]
42     [DebuggerDisplay("Count={Count}")]
43     public class AsyncCache<TKey, TValue> : ICollection<KeyValuePair<TKey,Task<TValue>>>
44     {
45         /// <summary>The factory to use to create tasks.</summary>
46         private readonly Func<TKey, Task<TValue>> _valueFactory;
47         /// <summary>The dictionary to store all of the tasks.</summary>
48         private readonly ConcurrentDictionary<TKey, Lazy<Task<TValue>>> _map;
49
50         /// <summary>Initializes the cache.</summary>
51         /// <param name="valueFactory">A factory for producing the cache's values.</param>
52         public AsyncCache(Func<TKey, Task<TValue>> valueFactory)
53         {
54             if (valueFactory == null) throw new ArgumentNullException("loader");
55             _valueFactory = valueFactory;
56             _map = new ConcurrentDictionary<TKey, Lazy<Task<TValue>>>();
57         }
58
59         /// <summary>Gets a Task to retrieve the value for the specified key.</summary>
60         /// <param name="key">The key whose value should be retrieved.</param>
61         /// <returns>A Task for the value of the specified key.</returns>
62         public Task<TValue> GetValue(TKey key)
63         {
64             if (key == null) throw new ArgumentNullException("key");
65             var value = new Lazy<Task<TValue>>(() => _valueFactory(key));
66             return _map.GetOrAdd(key, value).Value;
67         }
68
69         /// <summary>Sets the value for the specified key.</summary>
70         /// <param name="key">The key whose value should be set.</param>
71         /// <param name="value">The value to which the key should be set.</param>
72         public void SetValue(TKey key, TValue value)
73         {
74             SetValue(key, Task.Factory.FromResult(value));
75         }
76
77         /// <summary>Sets the value for the specified key.</summary>
78         /// <param name="key">The key whose value should be set.</param>
79         /// <param name="value">The value to which the key should be set.</param>
80         public void SetValue(TKey key, Task<TValue> value)
81         {
82             if (key == null) throw new ArgumentNullException("key");
83             _map[key] = LazyExtensions.Create(value);
84         }
85
86         /// <summary>Gets a Task to retrieve the value for the specified key.</summary>
87         /// <param name="key">The key whose value should be retrieved.</param>
88         /// <returns>A Task for the value of the specified key.</returns>
89         public Task<TValue> this[TKey key]
90         {
91             get { return GetValue(key); }
92             set { SetValue(key, value); }
93         }
94
95         /// <summary>Empties the cache.</summary>
96         public void Clear() { _map.Clear(); }
97
98         /// <summary>Gets the number of items in the cache.</summary>
99         public int Count { get { return _map.Count; } }
100
101         /// <summary>Gets an enumerator for the contents of the cache.</summary>
102         /// <returns>An enumerator for the contents of the cache.</returns>
103         public IEnumerator<KeyValuePair<TKey, Task<TValue>>> GetEnumerator()
104         {
105             return _map.Select(p => new KeyValuePair<TKey, Task<TValue>>(p.Key, p.Value.Value)).GetEnumerator();
106         }
107
108         /// <summary>Gets an enumerator for the contents of the cache.</summary>
109         /// <returns>An enumerator for the contents of the cache.</returns>
110         IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
111
112         /// <summary>Adds or overwrites the specified entry in the cache.</summary>
113         /// <param name="item">The item to be added.</param>
114         void ICollection<KeyValuePair<TKey, Task<TValue>>>.Add(KeyValuePair<TKey, Task<TValue>> item)
115         {
116             this[item.Key] = item.Value;
117         }
118
119         /// <summary>Determines whether the cache contains the specified key.</summary>
120         /// <param name="item">The item contained the key to be searched for.</param>
121         /// <returns>True if the cache contains the key; otherwise, false.</returns>
122         bool ICollection<KeyValuePair<TKey, Task<TValue>>>.Contains(KeyValuePair<TKey, Task<TValue>> item)
123         {
124             return _map.ContainsKey(item.Key);
125         }
126
127         /// <summary>
128         /// Copies the elements of the System.Collections.Generic.ICollection<T> to an
129         /// System.Array, starting at a particular System.Array index.
130         /// </summary>
131         /// <param name="array">
132         /// The one-dimensional System.Array that is the destination of the elements
133         /// copied from System.Collections.Generic.ICollection<T>. The System.Array must
134         /// have zero-based indexing.
135         /// </param>
136         /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
137         void ICollection<KeyValuePair<TKey, Task<TValue>>>.CopyTo(KeyValuePair<TKey, Task<TValue>>[] array, int arrayIndex)
138         {
139             ((ICollection<KeyValuePair<TKey, Task<TValue>>>)_map).CopyTo(array, arrayIndex);
140         }
141
142         /// <summary>Gets whether the cache is read-only.</summary>
143         bool ICollection<KeyValuePair<TKey, Task<TValue>>>.IsReadOnly { get { return false; } }
144
145         /// <summary>Removes the specified key from the cache.</summary>
146         /// <param name="item">The item containing the key to be removed.</param>
147         /// <returns>True if the item could be removed; otherwise, false.</returns>
148         bool ICollection<KeyValuePair<TKey, Task<TValue>>>.Remove(KeyValuePair<TKey, Task<TValue>> item)
149         {
150             Lazy<Task<TValue>> value;
151             return _map.TryRemove(item.Key, out value);
152         }
153     }
154
155     /// <summary>An asynchronous cache for downloaded HTML.</summary>
156     public sealed class HtmlAsyncCache : AsyncCache<Uri, string>
157     {
158         /// <summary>Initializes the HtmlCache.</summary>
159         public HtmlAsyncCache() :
160             base(uri => new WebClient().DownloadStringTask(uri)) { }
161     }
162 }