2
Copyright (C) 2008 Jeroen Frijters
4
This software is provided 'as-is', without any express or implied
5
warranty. In no event will the authors be held liable for any damages
6
arising from the use of this software.
8
Permission is granted to anyone to use this software for any purpose,
9
including commercial applications, and to alter it and redistribute it
10
freely, subject to the following restrictions:
12
1. The origin of this software must not be misrepresented; you must not
13
claim that you wrote the original software. If you use this software
14
in a product, an acknowledgment in the product documentation would be
15
appreciated but is not required.
16
2. Altered source versions must be plainly marked as such, and must not be
17
misrepresented as being the original software.
18
3. This notice may not be removed or altered from any source distribution.
25
using System.Collections.Generic;
26
using System.Runtime.CompilerServices;
28
namespace IKVM.Internal
31
* This is a special purpose dictionary. It only keeps weak references to the keys,
32
* thereby making them eligable for GC at any time.
34
* However, it does not make any promises about cleaning up the values (hence "passive").
35
* This class should only be used if memory consumption and lifetime of the values is of no consequence.
37
* Furthermore, the type also assumes that it needs to continue to work during AppDomain unload,
38
* meaning that it uses GC.SuppressFinalize() on the WeakReference objects it uses to prevent
39
* them from being finalized during AppDomain unload.
40
* As a consequence, if you want to dispose on object of this type prior to AppDomain unload, you *must*
41
* call Dispose() or you will leak GC handles. After calling Dispose() you should not call any other
44
* This class is thread safe and TryGetValue is lock free.
47
sealed class PassiveWeakDictionary<TKey, TValue>
51
private volatile Bucket[] table = new Bucket[37];
53
private FreeNode freeList;
54
private int freeCount;
56
internal void Dispose()
58
Bucket[] table = this.table;
59
for (int i = 0; i < table.Length; i++)
61
for (Bucket b = table[i]; b != null; b = b.Next)
63
GC.ReRegisterForFinalize(b.WeakRef);
66
for (FreeNode b = freeList; b != null; b = b.Next)
68
GC.ReRegisterForFinalize(b.WeakRef);
72
internal void Add(TKey key, TValue value)
75
throw new ArgumentNullException("key");
79
if (count > table.Length)
83
int index = GetHashCode(key) % table.Length;
84
for (Bucket b = table[index]; b != null; b = b.Next)
88
throw new ArgumentException("Key already exists");
91
table[index] = new Bucket(AllocWeakRef(key), value, table[index]);
96
private WeakReference AllocWeakRef(TKey key)
98
WeakReference weakRef;
101
weakRef = freeList.WeakRef;
102
freeList = freeList.Next;
104
weakRef.Target = key;
108
weakRef = new WeakReference(key, true);
109
GC.SuppressFinalize(weakRef);
114
private static readonly int[] primes = {
115
37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411, 32771, 65537, 131101, 262147, 524309, 1048583, 2097169,
116
4194319, 8388617, 16777259, 33554467, 67108879, 134217757, 268435459, 536870923, 1073741827, 2147483647 };
118
private static int GetPrime(int count)
120
foreach (int p in primes)
128
private static int GetHashCode(TKey key)
130
return RuntimeHelpers.GetHashCode(key) & 0x7FFFFFFF;
133
private void Rehash()
136
for (int i = 0; i < table.Length; i++)
138
for (Bucket b = table[i]; b != null; b = b.Next)
146
Bucket[] newTable = new Bucket[GetPrime(count * 2)];
147
for (int i = 0; i < table.Length; i++)
149
for (Bucket b = table[i]; b != null; b = b.Next)
154
int index = GetHashCode(key) % newTable.Length;
155
WeakReference weakRef = AllocWeakRef(key);
156
newTable[index] = new Bucket(weakRef, b.Value, newTable[index]);
160
// don't cache an infinite amount,
161
// busy loop garbage allocation benchmarking shows that 8K is enough with current gen0 size
162
if (freeCount < 8192)
164
WeakReference weakRef = b.WeakRef;
166
freeList = new FreeNode(weakRef, freeList);
171
GC.ReRegisterForFinalize(b.WeakRef);
177
this.table = newTable;
180
internal bool TryGetValue(TKey key, out TValue value)
183
throw new ArgumentNullException("key");
185
Bucket[] table = this.table;
186
int index = GetHashCode(key) % table.Length;
187
for (Bucket b = table[index]; b != null; b = b.Next)
199
private sealed class Bucket
201
private volatile WeakReference weakRef;
202
internal TValue Value;
203
internal readonly Bucket Next;
205
internal Bucket(WeakReference weakRef, TValue value, Bucket next)
207
this.weakRef = weakRef;
212
internal WeakReference WeakRef
224
WeakReference weakRef = this.weakRef;
227
TKey key = (TKey)weakRef.Target;
228
// this extra null check is to guard against the case where there was a GC and a Rehash and the weak ref was reused for another slot
229
// between the initial read of weakRef and accessing its Target property
230
if (this.weakRef != null)
239
internal void Clear()
245
private sealed class FreeNode
247
internal readonly WeakReference WeakRef;
248
internal readonly FreeNode Next;
250
internal FreeNode(WeakReference weakRef, FreeNode next)
252
this.WeakRef = weakRef;