2 // ConditionalWeakTable.cs
5 // Rodrigo Kumpera (rkumpera@novell.com)
7 // Copyright (C) 2010 Novell, Inc (http://www.novell.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections;
31 using System.Collections.Generic;
33 namespace System.Runtime.CompilerServices
35 internal struct Ephemeron
38 internal object value;
43 The runtime need to inform the table about how many entries were expired.
44 Compact the table when there are too many tombstones.
45 Rehash to a smaller size when there are too few entries.
46 Change rehash condition check to use non-fp code.
47 Look into using quatratic probing/double hashing to reduce clustering problems.
48 Make reads and non-expanding writes (add/remove) lock free.
50 public sealed class ConditionalWeakTable<TKey, TValue>
54 const int INITIAL_SIZE = 13;
55 const float LOAD_FACTOR = 0.7f;
58 object _lock = new object ();
61 public delegate TValue CreateValueCallback (TKey key);
63 public ConditionalWeakTable ()
65 data = new Ephemeron [INITIAL_SIZE];
66 GC.register_ephemeron_array (data);
69 ~ConditionalWeakTable ()
73 /*LOCKING: _lock must be held*/
75 uint newSize = (uint)HashHelpers.GetPrime ((data.Length << 1) | 1);
76 //Console.WriteLine ("--- resizing from {0} to {1}", data.Length, newSize);
78 Ephemeron[] tmp = new Ephemeron [newSize];
79 GC.register_ephemeron_array (tmp);
82 for (int i = 0; i < data.Length; ++i) {
83 object key = data[i].key;
84 object value = data[i].value;
85 if (key == null || key == GC.EPHEMERON_TOMBSTONE)
92 idx = initial_idx = (RuntimeHelpers.GetHashCode (key) & int.MaxValue) % len;
95 object k = tmp [idx].key;
97 //keys might be GC'd during Rehash
98 if (k == null || k == GC.EPHEMERON_TOMBSTONE) {
103 if (++idx == len) //Wrap around
105 } while (idx != initial_idx);
107 tmp [free_slot].key = key;
108 tmp [free_slot].value = value;
115 public void Add (TKey key, TValue value)
117 if (key == default (TKey))
118 throw new ArgumentNullException ("Null key", "key");
121 if (size >= data.Length * LOAD_FACTOR)
124 int len = data.Length;
128 idx = initial_idx = (RuntimeHelpers.GetHashCode (key) & int.MaxValue) % len;
130 object k = data [idx].key;
136 } else if (k == GC.EPHEMERON_TOMBSTONE && free_slot == -1) { //Add requires us to check for dupes :(
138 } else if (k == key) {
139 throw new ArgumentException ("Key already in the list", "key");
142 if (++idx == len) //Wrap around
144 } while (idx != initial_idx);
146 data [free_slot].key = key;
147 data [free_slot].value = value;
152 public bool Remove (TKey key)
154 if (key == default (TKey))
155 throw new ArgumentNullException ("Null key", "key");
158 int len = data.Length;
159 int idx, initial_idx;
160 idx = initial_idx = (RuntimeHelpers.GetHashCode (key) & int.MaxValue) % len;
162 object k = data[idx].key;
164 data [idx].key = GC.EPHEMERON_TOMBSTONE;
165 data [idx].value = null;
171 if (++idx == len) //Wrap around
173 } while (idx != initial_idx);
178 public bool TryGetValue (TKey key, out TValue value)
181 throw new ArgumentNullException ("Null key", "key");
183 value = default (TValue);
185 int len = data.Length;
186 int idx, initial_idx;
187 idx = initial_idx = (RuntimeHelpers.GetHashCode (key) & int.MaxValue) % len;
190 object k = data [idx].key;
192 value = (TValue)data [idx].value;
197 if (++idx == len) //Wrap around
199 } while (idx != initial_idx);
204 public TValue GetOrCreateValue (TKey key)
206 return GetValue (key, k => Activator.CreateInstance<TValue> ());
209 public TValue GetValue (TKey key, CreateValueCallback createValueCallback)
211 if (createValueCallback == null)
212 throw new ArgumentNullException ("Null create delegate", "createValueCallback");
217 if (TryGetValue (key, out res))
220 res = createValueCallback (key);
227 // extracted from ../../../../external/referencesource/mscorlib/system/runtime/compilerservices/
228 internal ICollection<TKey> Keys
230 [System.Security.SecuritySafeCritical]
233 List<TKey> list = new List<TKey>(data.Length);
236 for (int i = 0; i < data.Length; ++i)
238 TKey key = (TKey) data [i].key;