1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | |
6 | #ifndef __COMReflectionCache_hpp__ |
7 | #define __COMReflectionCache_hpp__ |
8 | |
9 | #include <stddef.h> |
10 | #include "class.h" |
11 | #include "threads.h" |
12 | #include "simplerwlock.hpp" |
13 | |
14 | class ReflectClass; |
15 | |
16 | template <class Element, class CacheType, int CacheSize> class ReflectionCache |
17 | : public SimpleRWLock |
18 | { |
19 | public: |
20 | ReflectionCache() |
21 | : SimpleRWLock(COOPERATIVE, LOCK_REFLECTCACHE) |
22 | { |
23 | WRAPPER_NO_CONTRACT; |
24 | index = 0; |
25 | currentStamp = 0; |
26 | } |
27 | |
28 | void Init(); |
29 | |
30 | BOOL GetFromCache(Element *pElement, CacheType& rv) |
31 | { |
32 | CONTRACTL |
33 | { |
34 | NOTHROW; |
35 | GC_NOTRIGGER; |
36 | MODE_ANY; |
37 | PRECONDITION(CheckPointer(pElement)); |
38 | } |
39 | CONTRACTL_END; |
40 | |
41 | this->EnterRead(); |
42 | |
43 | rv = 0; |
44 | int i = SlotInCache(pElement); |
45 | BOOL fGotIt = (i != CacheSize); |
46 | if (fGotIt) |
47 | { |
48 | rv = m_pResult[i].element.GetValue(); |
49 | m_pResult[i].stamp = InterlockedIncrement(¤tStamp); |
50 | } |
51 | this->LeaveRead(); |
52 | |
53 | if (fGotIt) |
54 | AdjustStamp(FALSE); |
55 | |
56 | return fGotIt; |
57 | } |
58 | |
59 | void AddToCache(Element *pElement, CacheType obj) |
60 | { |
61 | CONTRACTL |
62 | { |
63 | NOTHROW; |
64 | GC_NOTRIGGER; |
65 | MODE_ANY; |
66 | PRECONDITION(CheckPointer(pElement)); |
67 | } |
68 | CONTRACTL_END; |
69 | |
70 | this->EnterWrite(); |
71 | currentStamp++; // no need to InterlockIncrement b/c write lock taken |
72 | int i = SlotInCache(pElement); |
73 | if (i == CacheSize) |
74 | { |
75 | int slot = index; |
76 | // Not in cache. |
77 | if (slot == CacheSize) |
78 | { |
79 | // Reuse a slot. |
80 | slot = 0; |
81 | LONG minStamp = m_pResult[0].stamp; |
82 | for (i = 1; i < CacheSize; i ++) |
83 | { |
84 | if (m_pResult[i].stamp < minStamp) |
85 | { |
86 | slot = i; |
87 | minStamp = m_pResult[i].stamp; |
88 | } |
89 | } |
90 | } |
91 | else |
92 | m_pResult[slot].element.InitValue(); |
93 | |
94 | m_pResult[slot].element = *pElement; |
95 | m_pResult[slot].element.SetValue(obj); |
96 | m_pResult[slot].stamp = currentStamp; |
97 | |
98 | UpdateHashTable(pElement->GetHash(), slot); |
99 | |
100 | if (index < CacheSize) |
101 | index++; |
102 | } |
103 | AdjustStamp(TRUE); |
104 | this->LeaveWrite(); |
105 | } |
106 | |
107 | private: |
108 | // Lock must have been taken before calling this. |
109 | int SlotInCache(Element *pElement) |
110 | { |
111 | CONTRACTL |
112 | { |
113 | NOTHROW; |
114 | GC_NOTRIGGER; |
115 | MODE_ANY; |
116 | PRECONDITION(LockTaken()); |
117 | PRECONDITION(CheckPointer(pElement)); |
118 | } |
119 | CONTRACTL_END; |
120 | |
121 | if (index == 0) |
122 | return CacheSize; |
123 | |
124 | size_t hash = pElement->GetHash(); |
125 | |
126 | int slot = m_pHashTable[hash%CacheSize].slot; |
127 | if (slot == -1) |
128 | return CacheSize; |
129 | |
130 | if (m_pResult[slot].element == *pElement) |
131 | return slot; |
132 | |
133 | for (int i = 0; i < index; i ++) |
134 | { |
135 | if (i != slot && m_pHashTable[i].hash == hash) |
136 | { |
137 | if (m_pResult[i].element == *pElement) |
138 | return i; |
139 | } |
140 | } |
141 | |
142 | return CacheSize; |
143 | } |
144 | |
145 | void AdjustStamp(BOOL hasWriterLock) |
146 | { |
147 | CONTRACTL |
148 | { |
149 | NOTHROW; |
150 | GC_NOTRIGGER; |
151 | MODE_ANY; |
152 | } |
153 | CONTRACTL_END; |
154 | |
155 | if ((currentStamp & 0x40000000) == 0) |
156 | return; |
157 | if (!hasWriterLock) |
158 | { |
159 | _ASSERTE (!LockTaken()); |
160 | this->EnterWrite(); |
161 | } |
162 | else |
163 | _ASSERTE (IsWriterLock()); |
164 | |
165 | if (currentStamp & 0x40000000) |
166 | { |
167 | currentStamp >>= 1; |
168 | for (int i = 0; i < index; i ++) |
169 | m_pResult[i].stamp >>= 1; |
170 | } |
171 | if (!hasWriterLock) |
172 | this->LeaveWrite(); |
173 | } |
174 | |
175 | void UpdateHashTable(SIZE_T hash, int slot) |
176 | { |
177 | CONTRACTL |
178 | { |
179 | NOTHROW; |
180 | GC_NOTRIGGER; |
181 | MODE_ANY; |
182 | PRECONDITION(IsWriterLock()); |
183 | } |
184 | CONTRACTL_END; |
185 | |
186 | m_pHashTable[slot].hash = hash; |
187 | m_pHashTable[hash%CacheSize].slot = slot; |
188 | } |
189 | |
190 | struct CacheTable |
191 | { |
192 | Element element; |
193 | int stamp; |
194 | } *m_pResult; |
195 | |
196 | struct HashTable |
197 | { |
198 | size_t hash; // Record hash value for each slot |
199 | int slot; // The slot corresponding to the hash. |
200 | } *m_pHashTable; |
201 | |
202 | int index; |
203 | LONG currentStamp; |
204 | }; |
205 | |
206 | |
207 | #ifdef FEATURE_COMINTEROP |
208 | |
209 | #define ReflectionMaxCachedNameLength 23 |
210 | struct DispIDCacheElement |
211 | { |
212 | MethodTable *pMT; |
213 | int strNameLength; |
214 | LCID lcid; |
215 | DISPID DispId; |
216 | WCHAR strName[ReflectionMaxCachedNameLength+1]; |
217 | DispIDCacheElement () |
218 | : pMT (NULL), strNameLength(0), lcid(0), DispId(0) |
219 | { |
220 | LIMITED_METHOD_CONTRACT; |
221 | } |
222 | |
223 | BOOL operator==(const DispIDCacheElement& var) const |
224 | { |
225 | LIMITED_METHOD_CONTRACT; |
226 | return (pMT == var.pMT && strNameLength == var.strNameLength |
227 | && lcid == var.lcid && wcscmp (strName, var.strName) == 0); |
228 | } |
229 | |
230 | DispIDCacheElement& operator= (const DispIDCacheElement& var) |
231 | { |
232 | LIMITED_METHOD_CONTRACT; |
233 | _ASSERTE (var.strNameLength <= ReflectionMaxCachedNameLength); |
234 | pMT = var.pMT; |
235 | strNameLength = var.strNameLength; |
236 | lcid = var.lcid; |
237 | wcscpy_s (strName, COUNTOF(strName), var.strName); |
238 | return *this; |
239 | } |
240 | |
241 | DISPID GetValue () |
242 | { |
243 | LIMITED_METHOD_CONTRACT; |
244 | return DispId; |
245 | } |
246 | |
247 | void InitValue () |
248 | { |
249 | LIMITED_METHOD_CONTRACT; |
250 | } |
251 | |
252 | void SetValue (DISPID Id) |
253 | { |
254 | LIMITED_METHOD_CONTRACT; |
255 | DispId = Id; |
256 | } |
257 | |
258 | size_t GetHash () |
259 | { |
260 | LIMITED_METHOD_CONTRACT; |
261 | return (size_t)pMT + strNameLength + (lcid << 4); |
262 | } |
263 | }; |
264 | |
265 | typedef ReflectionCache <DispIDCacheElement, DISPID, 128> DispIDCache; |
266 | |
267 | #endif // FEATURE_COMINTEROP |
268 | |
269 | #endif // __COMReflectionCache_hpp__ |
270 | |