RecyclerWeakReference.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. namespace Js
  7. {
  8. template <typename TStrongRef> class RemoteWeakReference;
  9. };
  10. namespace Memory
  11. {
  12. // Forward declarations
  13. template <typename SizePolicy> class WeakReferenceHashTable;
  14. class Recycler;
  15. ///
  16. /// This class is represents a weak reference handle object
  17. /// It's a proxy to the actual strong reference itself
  18. /// This is an entry in the weak reference hash table.
  19. /// When a weak reference is created, the strong reference is set to
  20. /// point to an object.
  21. /// When the referenced object is collected, the strong reference is set to NULL
  22. ///
  23. /// Clients should not use this class but instead use RecyclerWeakReference<type>
  24. /// which provides for automatic type conversion of the underlying reference
  25. class RecyclerWeakReferenceBase
  26. {
  27. public:
  28. friend class Recycler;
  29. template <typename SizePolicy> friend class WeakReferenceHashTable;
  30. template <typename TStrongRef> friend class Js::RemoteWeakReference;
  31. protected:
  32. FieldNoBarrier(char*) strongRef;
  33. FieldNoBarrier(HeapBlock *) strongRefHeapBlock;
  34. FieldNoBarrier(HeapBlock *) weakRefHeapBlock;
  35. FieldNoBarrier(RecyclerWeakReferenceBase*) next;
  36. #if DBG
  37. #if ENABLE_RECYCLER_TYPE_TRACKING
  38. FieldNoBarrier(type_info const *) typeInfo;
  39. #endif
  40. #if defined TRACK_ALLOC && defined(PERF_COUNTERS)
  41. FieldNoBarrier(PerfCounter::Counter *) counter;
  42. #endif
  43. #endif
  44. };
  45. /// Wrapper class template that can be used to acquire the underlying strong reference from the weak reference
  46. template<typename T>
  47. class RecyclerWeakReference: public RecyclerWeakReferenceBase
  48. {
  49. public:
  50. // Fast get of the strong reference- this might return a wrong result if the recycler is in sweep so callers
  51. // should never call this during sweep
  52. inline T* FastGet() const
  53. {
  54. return ((T*) strongRef);
  55. }
  56. inline T* Get() const
  57. {
  58. char * ref = this->strongRef;
  59. return (T*)ref;
  60. }
  61. inline T** GetAddressOfStrongRef()
  62. {
  63. return (T**)&strongRef;
  64. }
  65. };
  66. template <class T>
  67. class WeakReferenceCache
  68. {
  69. private:
  70. RecyclerWeakReference<T> * weakReference;
  71. public:
  72. WeakReferenceCache() : weakReference(nullptr) {};
  73. RecyclerWeakReference<T> * GetWeakReference(Recycler * recycler)
  74. {
  75. RecyclerWeakReference<T> * weakRef = this->weakReference;
  76. if (weakRef == nullptr)
  77. {
  78. weakRef = recycler->CreateWeakReferenceHandle((T*)this);
  79. this->weakReference = weakRef;
  80. }
  81. return weakRef;
  82. }
  83. };
  84. ///
  85. /// Hashtable class that maps strong references to weak references
  86. /// This is slightly unique in that the weak reference is a complete entry in the hashtable
  87. /// but is treated as the value for the client. The key is the strong reference.
  88. /// The hash table is a standard closed-addressing hash table where the strong references are
  89. /// hashed into buckets, and then stored in that buckets corresponding doubly linked list
  90. /// The hash-table is resized when its load factor exceeds a constant
  91. /// The buckets are allocated using the HeapAllocator. Individual entries are recycler allocated.
  92. ///
  93. template <typename SizePolicy>
  94. class WeakReferenceHashTable
  95. {
  96. static const int MaxAverageChainLength = 1;
  97. HeapAllocator* allocator;
  98. RecyclerWeakReferenceBase** buckets;
  99. uint count;
  100. uint size;
  101. RecyclerWeakReferenceBase* freeList;
  102. int modFunctionIndex;
  103. public:
  104. WeakReferenceHashTable(uint size, HeapAllocator* allocator):
  105. count(0),
  106. size(0),
  107. modFunctionIndex(UNKNOWN_MOD_INDEX),
  108. allocator(allocator),
  109. freeList(nullptr)
  110. {
  111. this->size = SizePolicy::GetSize(size, &modFunctionIndex);
  112. buckets = AllocatorNewArrayZ(HeapAllocator, allocator, RecyclerWeakReferenceBase*, this->size);
  113. }
  114. ~WeakReferenceHashTable()
  115. {
  116. AllocatorDeleteArray(HeapAllocator, allocator, size, buckets);
  117. }
  118. RecyclerWeakReferenceBase* Add(char* strongReference, Recycler * recycler)
  119. {
  120. uint targetBucket = HashKeyToBucket(strongReference, size);
  121. RecyclerWeakReferenceBase* entry = FindEntry(strongReference, targetBucket);
  122. if (entry != nullptr)
  123. {
  124. return entry;
  125. }
  126. return Create(strongReference, targetBucket, recycler);
  127. }
  128. bool FindOrAdd(char* strongReference, Recycler *recycler, RecyclerWeakReferenceBase **ppWeakRef)
  129. {
  130. Assert(ppWeakRef);
  131. uint targetBucket = HashKeyToBucket(strongReference, size);
  132. RecyclerWeakReferenceBase* entry = FindEntry(strongReference, targetBucket);
  133. if (entry != nullptr)
  134. {
  135. *ppWeakRef = entry;
  136. return false;
  137. }
  138. entry = Create(strongReference, targetBucket, recycler);
  139. *ppWeakRef = entry;
  140. return true;
  141. }
  142. #ifdef RECYCLER_TRACE_WEAKREF
  143. void DumpNode(RecyclerWeakReferenceBase* node)
  144. {
  145. Output::Print(_u("[ 0x%08x { 0x%08x, 0x%08x }]"), node, node->strongRef, mode->next);
  146. }
  147. void Dump()
  148. {
  149. RecyclerWeakReferenceBase *current;
  150. Output::Print(_u("HashTable with %d buckets and %d nodes\n"), this->size, this->count);
  151. for (uint i=0;i<size;i++) {
  152. Output::Print(_u("Bucket %d (0x%08x) ==> "), i, &buckets[i]);
  153. for (current = buckets[i] ; current != nullptr; current = current->next) {
  154. DumpNode(current);
  155. }
  156. Output::Print(_u("\n"));
  157. }
  158. }
  159. #endif
  160. bool TryGetValue(char* strongReference, RecyclerWeakReferenceBase** weakReference)
  161. {
  162. RecyclerWeakReferenceBase* current = FindEntry(strongReference, HashKeyToBucket(strongReference, size));
  163. if (current != nullptr)
  164. {
  165. *weakReference = current;
  166. return true;
  167. }
  168. return false;
  169. }
  170. void Remove(char* key, RecyclerWeakReferenceBase** pOut)
  171. {
  172. uint val = HashKeyToBucket(key, size);
  173. RecyclerWeakReferenceBase ** pprev = &buckets[val];
  174. RecyclerWeakReferenceBase *current = *pprev;
  175. while (current)
  176. {
  177. if (DefaultComparer<char*>::Equals(key, current->strongRef))
  178. {
  179. *pprev = current->next;
  180. if (pOut != nullptr)
  181. {
  182. (*pOut) = current;
  183. }
  184. count--;
  185. #ifdef RECYCLER_TRACE_WEAKREF
  186. Output::Print(_u("Remove 0x%08x to bucket %d, count is %d\n"), current, val, count);
  187. #endif
  188. break;
  189. }
  190. pprev = &current->next;
  191. current = *pprev;
  192. }
  193. }
  194. void Remove(char* key)
  195. {
  196. Remove(key, nullptr);
  197. }
  198. template <class Func>
  199. void Map(Func fn)
  200. {
  201. uint removed = 0;
  202. #if DEBUG
  203. uint countedEntries = 0;
  204. #endif
  205. for (uint i=0;i<size;i++)
  206. {
  207. RecyclerWeakReferenceBase ** pprev = &buckets[i];
  208. RecyclerWeakReferenceBase *current = *pprev;
  209. while (current)
  210. {
  211. if (fn(current))
  212. {
  213. pprev = &current->next;
  214. #if DEBUG
  215. countedEntries++;
  216. #endif
  217. }
  218. else
  219. {
  220. // remove
  221. *pprev = current->next;
  222. removed++;
  223. }
  224. current = *pprev;
  225. }
  226. }
  227. Assert(removed <= count);
  228. count -= removed;
  229. #if DEBUG
  230. Assert(countedEntries == count);
  231. #endif
  232. }
  233. private:
  234. // If density is a compile-time constant, then we can optimize (avoids division)
  235. // Sometimes the compiler can also make this optimization, but this way is guaranteed.
  236. template< uint density > bool IsDenserThan() const
  237. {
  238. return count > (size * density);
  239. }
  240. RecyclerWeakReferenceBase * FindEntry(char* strongReference, uint targetBucket)
  241. {
  242. for (RecyclerWeakReferenceBase * current = buckets[targetBucket] ; current != nullptr; current = current->next)
  243. {
  244. if (DefaultComparer<char*>::Equals(strongReference, current->strongRef))
  245. {
  246. return current;
  247. }
  248. }
  249. return nullptr;
  250. }
  251. hash_t HashKeyToBucket(char* strongReference, int size)
  252. {
  253. hash_t hashCode = DefaultComparer<char*>::GetHashCode(strongReference);
  254. return SizePolicy::GetBucket(hashCode, size, modFunctionIndex);
  255. }
  256. void AddEntry(RecyclerWeakReferenceBase* entry, RecyclerWeakReferenceBase** bucket)
  257. {
  258. RecyclerWeakReferenceBase* first = (*bucket);
  259. entry->next = first;
  260. (*bucket) = entry;
  261. }
  262. void Resize(int newSize)
  263. {
  264. #if DEBUG
  265. uint copiedEntries = 0;
  266. #endif
  267. RecyclerWeakReferenceBase** newBuckets = AllocatorNewArrayZ(HeapAllocator, allocator, RecyclerWeakReferenceBase*, newSize);
  268. for (uint i=0; i < size; i++)
  269. {
  270. RecyclerWeakReferenceBase* current = buckets[i];
  271. while (current != nullptr)
  272. {
  273. int targetBucket = HashKeyToBucket(current->strongRef, newSize);
  274. RecyclerWeakReferenceBase* next = current->next; // Cache the next pointer
  275. AddEntry(current, &newBuckets[targetBucket]);
  276. #if DEBUG
  277. copiedEntries++;
  278. #endif
  279. current = next;
  280. }
  281. }
  282. AllocatorDeleteArray(HeapAllocator, allocator, size, buckets);
  283. size = newSize;
  284. buckets = newBuckets;
  285. #if DEBUG
  286. Assert(this->count == copiedEntries);
  287. #endif
  288. }
  289. RecyclerWeakReferenceBase* Create(char* strongReference, uint targetBucket, Recycler * recycler)
  290. {
  291. Assert(HashKeyToBucket(strongReference, size) == targetBucket);
  292. Assert(!FindEntry(strongReference, targetBucket));
  293. if (IsDenserThan<MaxAverageChainLength>())
  294. {
  295. #ifdef RECYCLER_TRACE_WEAKREF
  296. Output::Print(_u("Count is %d\n"), this->count);
  297. #endif
  298. Resize(SizePolicy::GetSize(size*2, &modFunctionIndex));
  299. // After resize - we will need to recalculate the bucket
  300. targetBucket = HashKeyToBucket(strongReference, size);
  301. }
  302. RecyclerWeakReferenceBase* entry;
  303. entry = AllocatorNewBase(Recycler, recycler, AllocWeakReferenceEntry, RecyclerWeakReferenceBase);
  304. entry->strongRef = strongReference;
  305. entry->strongRefHeapBlock = recycler->FindHeapBlock(strongReference);
  306. Assert(entry->strongRefHeapBlock != nullptr);
  307. HeapBlock * weakRefHeapBlock = recycler->FindHeapBlock(entry);
  308. Assert(!weakRefHeapBlock->IsLargeHeapBlock() || ((LargeHeapBlock*)weakRefHeapBlock)->InPageHeapMode());
  309. entry->weakRefHeapBlock = weakRefHeapBlock;
  310. #ifdef RECYCLER_TRACE_WEAKREF
  311. Output::Print(_u("Add WeakRef 0x%08x for StrongRef %p to bucket %d\n"), entry, strongReference, targetBucket);
  312. #endif
  313. AddEntry(entry, &buckets[targetBucket]);
  314. count++;
  315. #if DBG
  316. #if ENABLE_RECYCLER_TYPE_TRACKING
  317. entry->typeInfo = nullptr;
  318. #endif
  319. #if defined(TRACK_ALLOC) && defined(PERF_COUNTERS)
  320. entry->counter = nullptr;
  321. #endif
  322. #endif
  323. return entry;
  324. }
  325. };
  326. }