RecyclerWeakReference.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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. uint Count()
  195. {
  196. return count;
  197. }
  198. void Remove(char* key)
  199. {
  200. Remove(key, nullptr);
  201. }
  202. template <class Func>
  203. void Map(Func fn)
  204. {
  205. uint removed = 0;
  206. #if DEBUG
  207. uint countedEntries = 0;
  208. #endif
  209. for (uint i=0;i<size;i++)
  210. {
  211. RecyclerWeakReferenceBase ** pprev = &buckets[i];
  212. RecyclerWeakReferenceBase *current = *pprev;
  213. while (current)
  214. {
  215. if (fn(current))
  216. {
  217. pprev = &current->next;
  218. #if DEBUG
  219. countedEntries++;
  220. #endif
  221. }
  222. else
  223. {
  224. // remove
  225. *pprev = current->next;
  226. removed++;
  227. }
  228. current = *pprev;
  229. }
  230. }
  231. Assert(removed <= count);
  232. count -= removed;
  233. #if DEBUG
  234. Assert(countedEntries == count);
  235. #endif
  236. }
  237. private:
  238. // If density is a compile-time constant, then we can optimize (avoids division)
  239. // Sometimes the compiler can also make this optimization, but this way is guaranteed.
  240. template< uint density > bool IsDenserThan() const
  241. {
  242. return count > (size * density);
  243. }
  244. RecyclerWeakReferenceBase * FindEntry(char* strongReference, uint targetBucket)
  245. {
  246. for (RecyclerWeakReferenceBase * current = buckets[targetBucket] ; current != nullptr; current = current->next)
  247. {
  248. if (DefaultComparer<char*>::Equals(strongReference, current->strongRef))
  249. {
  250. return current;
  251. }
  252. }
  253. return nullptr;
  254. }
  255. hash_t HashKeyToBucket(char* strongReference, int size)
  256. {
  257. hash_t hashCode = DefaultComparer<char*>::GetHashCode(strongReference);
  258. return SizePolicy::GetBucket(hashCode, size, modFunctionIndex);
  259. }
  260. void AddEntry(RecyclerWeakReferenceBase* entry, RecyclerWeakReferenceBase** bucket)
  261. {
  262. RecyclerWeakReferenceBase* first = (*bucket);
  263. entry->next = first;
  264. (*bucket) = entry;
  265. }
  266. void Resize(int newSize)
  267. {
  268. #if DEBUG
  269. uint copiedEntries = 0;
  270. #endif
  271. RecyclerWeakReferenceBase** newBuckets = AllocatorNewArrayZ(HeapAllocator, allocator, RecyclerWeakReferenceBase*, newSize);
  272. for (uint i=0; i < size; i++)
  273. {
  274. RecyclerWeakReferenceBase* current = buckets[i];
  275. while (current != nullptr)
  276. {
  277. int targetBucket = HashKeyToBucket(current->strongRef, newSize);
  278. RecyclerWeakReferenceBase* next = current->next; // Cache the next pointer
  279. AddEntry(current, &newBuckets[targetBucket]);
  280. #if DEBUG
  281. copiedEntries++;
  282. #endif
  283. current = next;
  284. }
  285. }
  286. AllocatorDeleteArray(HeapAllocator, allocator, size, buckets);
  287. size = newSize;
  288. buckets = newBuckets;
  289. #if DEBUG
  290. Assert(this->count == copiedEntries);
  291. #endif
  292. }
  293. RecyclerWeakReferenceBase* Create(char* strongReference, uint targetBucket, Recycler * recycler)
  294. {
  295. Assert(HashKeyToBucket(strongReference, size) == targetBucket);
  296. Assert(!FindEntry(strongReference, targetBucket));
  297. if (IsDenserThan<MaxAverageChainLength>())
  298. {
  299. #ifdef RECYCLER_TRACE_WEAKREF
  300. Output::Print(_u("Count is %d\n"), this->count);
  301. #endif
  302. Resize(SizePolicy::GetSize(size*2, &modFunctionIndex));
  303. // After resize - we will need to recalculate the bucket
  304. targetBucket = HashKeyToBucket(strongReference, size);
  305. }
  306. RecyclerWeakReferenceBase* entry;
  307. entry = AllocatorNewBase(Recycler, recycler, AllocWeakReferenceEntry, RecyclerWeakReferenceBase);
  308. entry->strongRef = strongReference;
  309. entry->strongRefHeapBlock = recycler->FindHeapBlock(strongReference);
  310. Assert(entry->strongRefHeapBlock != nullptr);
  311. HeapBlock * weakRefHeapBlock = recycler->FindHeapBlock(entry);
  312. Assert(!weakRefHeapBlock->IsLargeHeapBlock() || ((LargeHeapBlock*)weakRefHeapBlock)->InPageHeapMode());
  313. entry->weakRefHeapBlock = weakRefHeapBlock;
  314. #ifdef RECYCLER_TRACE_WEAKREF
  315. Output::Print(_u("Add WeakRef 0x%08x for StrongRef %p to bucket %d\n"), entry, strongReference, targetBucket);
  316. #endif
  317. AddEntry(entry, &buckets[targetBucket]);
  318. count++;
  319. #if DBG
  320. #if ENABLE_RECYCLER_TYPE_TRACKING
  321. entry->typeInfo = nullptr;
  322. #endif
  323. #if defined(TRACK_ALLOC) && defined(PERF_COUNTERS)
  324. entry->counter = nullptr;
  325. #endif
  326. #endif
  327. return entry;
  328. }
  329. };
  330. }