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
14class ReflectClass;
15
16template <class Element, class CacheType, int CacheSize> class ReflectionCache
17: public SimpleRWLock
18{
19public:
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(&currentStamp);
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
107private:
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
210struct 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
265typedef ReflectionCache <DispIDCacheElement, DISPID, 128> DispIDCache;
266
267#endif // FEATURE_COMINTEROP
268
269#endif // __COMReflectionCache_hpp__
270