RecyclerWeakReference.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. public:
  103. WeakReferenceHashTable(uint size, HeapAllocator* allocator):
  104. count(0),
  105. size(0),
  106. allocator(allocator),
  107. freeList(nullptr)
  108. {
  109. this->size = SizePolicy::GetSize(size);
  110. buckets = AllocatorNewArrayZ(HeapAllocator, allocator, RecyclerWeakReferenceBase*, this->size);
  111. }
  112. ~WeakReferenceHashTable()
  113. {
  114. AllocatorDeleteArray(HeapAllocator, allocator, size, buckets);
  115. }
  116. RecyclerWeakReferenceBase* Add(char* strongReference, Recycler * recycler)
  117. {
  118. uint targetBucket = HashKeyToBucket(strongReference, size);
  119. RecyclerWeakReferenceBase* entry = FindEntry(strongReference, targetBucket);
  120. if (entry != nullptr)
  121. {
  122. return entry;
  123. }
  124. return Create(strongReference, targetBucket, recycler);
  125. }
  126. bool FindOrAdd(char* strongReference, Recycler *recycler, RecyclerWeakReferenceBase **ppWeakRef)
  127. {
  128. Assert(ppWeakRef);
  129. uint targetBucket = HashKeyToBucket(strongReference, size);
  130. RecyclerWeakReferenceBase* entry = FindEntry(strongReference, targetBucket);
  131. if (entry != nullptr)
  132. {
  133. *ppWeakRef = entry;
  134. return false;
  135. }
  136. entry = Create(strongReference, targetBucket, recycler);
  137. *ppWeakRef = entry;
  138. return true;
  139. }
  140. #ifdef RECYCLER_TRACE_WEAKREF
  141. void DumpNode(RecyclerWeakReferenceBase* node)
  142. {
  143. Output::Print(_u("[ 0x%08x { 0x%08x, 0x%08x }]"), node, node->strongRef, mode->next);
  144. }
  145. void Dump()
  146. {
  147. RecyclerWeakReferenceBase *current;
  148. Output::Print(_u("HashTable with %d buckets and %d nodes\n"), this->size, this->count);
  149. for (uint i=0;i<size;i++) {
  150. Output::Print(_u("Bucket %d (0x%08x) ==> "), i, &buckets[i]);
  151. for (current = buckets[i] ; current != nullptr; current = current->next) {
  152. DumpNode(current);
  153. }
  154. Output::Print(_u("\n"));
  155. }
  156. }
  157. #endif
  158. bool TryGetValue(char* strongReference, RecyclerWeakReferenceBase** weakReference)
  159. {
  160. RecyclerWeakReferenceBase* current = FindEntry(strongReference, HashKeyToBucket(strongReference, size));
  161. if (current != nullptr)
  162. {
  163. *weakReference = current;
  164. return true;
  165. }
  166. return false;
  167. }
  168. void Remove(char* key, RecyclerWeakReferenceBase** pOut)
  169. {
  170. uint val = HashKeyToBucket(key, size);
  171. RecyclerWeakReferenceBase ** pprev = &buckets[val];
  172. RecyclerWeakReferenceBase *current = *pprev;
  173. while (current)
  174. {
  175. if (DefaultComparer<char*>::Equals(key, current->strongRef))
  176. {
  177. *pprev = current->next;
  178. if (pOut != nullptr)
  179. {
  180. (*pOut) = current;
  181. }
  182. count--;
  183. #ifdef RECYCLER_TRACE_WEAKREF
  184. Output::Print(_u("Remove 0x%08x to bucket %d, count is %d\n"), current, val, count);
  185. #endif
  186. break;
  187. }
  188. pprev = &current->next;
  189. current = *pprev;
  190. }
  191. }
  192. void Remove(char* key)
  193. {
  194. Remove(key, nullptr);
  195. }
  196. template <class Func>
  197. void Map(Func fn)
  198. {
  199. uint removed = 0;
  200. #if DEBUG
  201. uint countedEntries = 0;
  202. #endif
  203. for (uint i=0;i<size;i++)
  204. {
  205. RecyclerWeakReferenceBase ** pprev = &buckets[i];
  206. RecyclerWeakReferenceBase *current = *pprev;
  207. while (current)
  208. {
  209. if (fn(current))
  210. {
  211. pprev = &current->next;
  212. #if DEBUG
  213. countedEntries++;
  214. #endif
  215. }
  216. else
  217. {
  218. // remove
  219. *pprev = current->next;
  220. removed++;
  221. }
  222. current = *pprev;
  223. }
  224. }
  225. Assert(removed <= count);
  226. count -= removed;
  227. #if DEBUG
  228. Assert(countedEntries == count);
  229. #endif
  230. }
  231. private:
  232. // If density is a compile-time constant, then we can optimize (avoids division)
  233. // Sometimes the compiler can also make this optimization, but this way is guaranteed.
  234. template< uint density > bool IsDenserThan() const
  235. {
  236. return count > (size * density);
  237. }
  238. RecyclerWeakReferenceBase * FindEntry(char* strongReference, uint targetBucket)
  239. {
  240. for (RecyclerWeakReferenceBase * current = buckets[targetBucket] ; current != nullptr; current = current->next)
  241. {
  242. if (DefaultComparer<char*>::Equals(strongReference, current->strongRef))
  243. {
  244. return current;
  245. }
  246. }
  247. return nullptr;
  248. }
  249. uint HashKeyToBucket(char* strongReference, int size)
  250. {
  251. uint hashCode = DefaultComparer<char*>::GetHashCode(strongReference);
  252. return SizePolicy::GetBucket(hashCode, size);
  253. }
  254. void AddEntry(RecyclerWeakReferenceBase* entry, RecyclerWeakReferenceBase** bucket)
  255. {
  256. RecyclerWeakReferenceBase* first = (*bucket);
  257. entry->next = first;
  258. (*bucket) = entry;
  259. }
  260. void Resize(int newSize)
  261. {
  262. #if DEBUG
  263. uint copiedEntries = 0;
  264. #endif
  265. RecyclerWeakReferenceBase** newBuckets = AllocatorNewArrayZ(HeapAllocator, allocator, RecyclerWeakReferenceBase*, newSize);
  266. for (uint i=0; i < size; i++)
  267. {
  268. RecyclerWeakReferenceBase* current = buckets[i];
  269. while (current != nullptr)
  270. {
  271. int targetBucket = HashKeyToBucket(current->strongRef, newSize);
  272. RecyclerWeakReferenceBase* next = current->next; // Cache the next pointer
  273. AddEntry(current, &newBuckets[targetBucket]);
  274. #if DEBUG
  275. copiedEntries++;
  276. #endif
  277. current = next;
  278. }
  279. }
  280. AllocatorDeleteArray(HeapAllocator, allocator, size, buckets);
  281. size = newSize;
  282. buckets = newBuckets;
  283. #if DEBUG
  284. Assert(this->count == copiedEntries);
  285. #endif
  286. }
  287. RecyclerWeakReferenceBase* Create(char* strongReference, uint targetBucket, Recycler * recycler)
  288. {
  289. Assert(HashKeyToBucket(strongReference, size) == targetBucket);
  290. Assert(!FindEntry(strongReference, targetBucket));
  291. if (IsDenserThan<MaxAverageChainLength>())
  292. {
  293. #ifdef RECYCLER_TRACE_WEAKREF
  294. Output::Print(_u("Count is %d\n"), this->count);
  295. #endif
  296. Resize(SizePolicy::GetSize(size*2));
  297. // After resize - we will need to recalculate the bucket
  298. targetBucket = HashKeyToBucket(strongReference, size);
  299. }
  300. RecyclerWeakReferenceBase* entry;
  301. entry = AllocatorNewBase(Recycler, recycler, AllocWeakReferenceEntry, RecyclerWeakReferenceBase);
  302. entry->strongRef = strongReference;
  303. entry->strongRefHeapBlock = recycler->FindHeapBlock(strongReference);
  304. Assert(entry->strongRefHeapBlock != nullptr);
  305. HeapBlock * weakRefHeapBlock = recycler->FindHeapBlock(entry);
  306. Assert(!weakRefHeapBlock->IsLargeHeapBlock() || ((LargeHeapBlock*)weakRefHeapBlock)->InPageHeapMode());
  307. entry->weakRefHeapBlock = weakRefHeapBlock;
  308. #ifdef RECYCLER_TRACE_WEAKREF
  309. Output::Print(_u("Add 0x%08x to bucket %d\n"), entry, targetBucket);
  310. #endif
  311. AddEntry(entry, &buckets[targetBucket]);
  312. count++;
  313. #if DBG
  314. #if ENABLE_RECYCLER_TYPE_TRACKING
  315. entry->typeInfo = nullptr;
  316. #endif
  317. #if defined(TRACK_ALLOC) && defined(PERF_COUNTERS)
  318. entry->counter = nullptr;
  319. #endif
  320. #endif
  321. return entry;
  322. }
  323. };
  324. }