SmallHeapBlockAllocator.h 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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 Memory
  7. {
  8. template <typename TBlockType>
  9. class SmallHeapBlockAllocator
  10. {
  11. public:
  12. typedef TBlockType BlockType;
  13. SmallHeapBlockAllocator();
  14. void Initialize();
  15. template <ObjectInfoBits attributes>
  16. inline char * InlinedAlloc(Recycler * recycler, DECLSPEC_GUARD_OVERFLOW size_t sizeCat);
  17. // Pass through template parameter to InlinedAllocImpl
  18. template <bool canFaultInject>
  19. inline char * SlowAlloc(Recycler * recycler, DECLSPEC_GUARD_OVERFLOW size_t sizeCat, ObjectInfoBits attributes);
  20. // There are paths where we simply can't OOM here, so we shouldn't fault inject as it creates a bit of a mess
  21. template <bool canFaultInject>
  22. inline char* InlinedAllocImpl(Recycler * recycler, DECLSPEC_GUARD_OVERFLOW size_t sizeCat, ObjectInfoBits attributes);
  23. TBlockType * GetHeapBlock() const { return heapBlock; }
  24. SmallHeapBlockAllocator * GetNext() const { return next; }
  25. void Set(TBlockType * heapBlock);
  26. void SetNew(TBlockType * heapBlock);
  27. void Clear();
  28. void UpdateHeapBlock();
  29. void SetExplicitFreeList(FreeObject* list);
  30. static uint32 GetEndAddressOffset() { return offsetof(SmallHeapBlockAllocator, endAddress); }
  31. char *GetEndAddress() { return endAddress; }
  32. static uint32 GetFreeObjectListOffset() { return offsetof(SmallHeapBlockAllocator, freeObjectList); }
  33. FreeObject *GetFreeObjectList() { return freeObjectList; }
  34. void SetFreeObjectList(FreeObject *freeObject) { freeObjectList = freeObject; }
  35. #if defined(PROFILE_RECYCLER_ALLOC) || defined(RECYCLER_MEMORY_VERIFY) || defined(MEMSPECT_TRACKING) || defined(ETW_MEMORY_TRACKING)
  36. void SetTrackNativeAllocatedObjectCallBack(void (*pfnCallBack)(Recycler *, void *, size_t))
  37. {
  38. pfnTrackNativeAllocatedObjectCallBack = pfnCallBack;
  39. }
  40. #endif
  41. #if DBG
  42. FreeObject * GetExplicitFreeList() const
  43. {
  44. Assert(IsExplicitFreeObjectListAllocMode());
  45. return this->freeObjectList;
  46. }
  47. #endif
  48. bool IsBumpAllocMode() const
  49. {
  50. return endAddress != nullptr;
  51. }
  52. bool IsExplicitFreeObjectListAllocMode() const
  53. {
  54. return this->heapBlock == nullptr;
  55. }
  56. bool IsFreeListAllocMode() const
  57. {
  58. return !IsBumpAllocMode() && !IsExplicitFreeObjectListAllocMode();
  59. }
  60. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  61. bool IsAllocatingDuringConcurrentSweepMode(Recycler * recycler) const
  62. {
  63. return IsFreeListAllocMode() && recycler->IsConcurrentSweepState();
  64. }
  65. #endif
  66. private:
  67. static bool NeedSetAttributes(ObjectInfoBits attributes)
  68. {
  69. return attributes != LeafBit && (attributes & InternalObjectInfoBitMask) != 0;
  70. }
  71. char * endAddress;
  72. FreeObject * freeObjectList;
  73. TBlockType * heapBlock;
  74. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  75. #if DBG
  76. bool isAllocatingFromNewBlock;
  77. #endif
  78. #endif
  79. SmallHeapBlockAllocator * prev;
  80. SmallHeapBlockAllocator * next;
  81. friend class HeapBucketT<BlockType>;
  82. #ifdef RECYCLER_SLOW_CHECK_ENABLED
  83. template <class TBlockAttributes>
  84. friend class SmallHeapBlockT;
  85. #endif
  86. #if defined(PROFILE_RECYCLER_ALLOC) || defined(RECYCLER_MEMORY_VERIFY)
  87. HeapBucket * bucket;
  88. #endif
  89. #ifdef RECYCLER_TRACK_NATIVE_ALLOCATED_OBJECTS
  90. char * lastNonNativeBumpAllocatedBlock;
  91. void TrackNativeAllocatedObjects();
  92. #endif
  93. #if defined(PROFILE_RECYCLER_ALLOC) || defined(RECYCLER_MEMORY_VERIFY) || defined(MEMSPECT_TRACKING) || defined(ETW_MEMORY_TRACKING)
  94. void (*pfnTrackNativeAllocatedObjectCallBack)(Recycler * recycler, void *, size_t sizeCat);
  95. #endif
  96. };
  97. template <typename TBlockType>
  98. template <bool canFaultInject>
  99. inline char*
  100. SmallHeapBlockAllocator<TBlockType>::InlinedAllocImpl(Recycler * recycler, DECLSPEC_GUARD_OVERFLOW size_t sizeCat, ObjectInfoBits attributes)
  101. {
  102. Assert((attributes & InternalObjectInfoBitMask) == attributes);
  103. #ifdef RECYCLER_WRITE_BARRIER
  104. Assert(!CONFIG_FLAG(ForceSoftwareWriteBarrier) || (attributes & WithBarrierBit) || (attributes & LeafBit));
  105. #endif
  106. AUTO_NO_EXCEPTION_REGION;
  107. if (canFaultInject)
  108. {
  109. FAULTINJECT_MEMORY_NOTHROW(_u("InlinedAllocImpl"), sizeCat);
  110. }
  111. char * memBlock = (char *)freeObjectList;
  112. char * nextCurrentAddress = memBlock + sizeCat;
  113. char * endAddress = this->endAddress;
  114. if (nextCurrentAddress <= endAddress)
  115. {
  116. // Bump Allocation
  117. Assert(this->IsBumpAllocMode());
  118. #ifdef RECYCLER_TRACK_NATIVE_ALLOCATED_OBJECTS
  119. TrackNativeAllocatedObjects();
  120. lastNonNativeBumpAllocatedBlock = memBlock;
  121. #endif
  122. freeObjectList = (FreeObject *)nextCurrentAddress;
  123. if (NeedSetAttributes(attributes))
  124. {
  125. if ((attributes & (FinalizeBit | TrackBit)) != 0)
  126. {
  127. // Make sure a valid vtable is installed as once the attributes have been set this allocation may be traced by background marking
  128. memBlock = (char *)new (memBlock) DummyVTableObject();
  129. #if defined(_M_ARM32_OR_ARM64)
  130. // On ARM, make sure the v-table write is performed before setting the attributes
  131. MemoryBarrier();
  132. #endif
  133. }
  134. heapBlock->SetAttributes(memBlock, (attributes & StoredObjectInfoBitMask));
  135. }
  136. return memBlock;
  137. }
  138. if (memBlock != nullptr && endAddress == nullptr)
  139. {
  140. // Free list allocation
  141. freeObjectList = ((FreeObject *)memBlock)->GetNext();
  142. #ifdef RECYCLER_MEMORY_VERIFY
  143. ((FreeObject *)memBlock)->DebugFillNext();
  144. #endif
  145. Assert(!this->IsBumpAllocMode());
  146. if (NeedSetAttributes(attributes))
  147. {
  148. TBlockType * allocationHeapBlock = this->heapBlock;
  149. if (allocationHeapBlock == nullptr)
  150. {
  151. Assert(this->IsExplicitFreeObjectListAllocMode());
  152. allocationHeapBlock = (TBlockType *)recycler->FindHeapBlock(memBlock);
  153. Assert(allocationHeapBlock != nullptr);
  154. Assert(!allocationHeapBlock->IsLargeHeapBlock());
  155. }
  156. if ((attributes & (FinalizeBit | TrackBit)) != 0)
  157. {
  158. // Make sure a valid vtable is installed as once the attributes have been set this allocation may be traced by background marking
  159. memBlock = (char *)new (memBlock) DummyVTableObject();
  160. #if defined(_M_ARM32_OR_ARM64)
  161. // On ARM, make sure the v-table write is performed before setting the attributes
  162. MemoryBarrier();
  163. #endif
  164. }
  165. allocationHeapBlock->SetAttributes(memBlock, (attributes & StoredObjectInfoBitMask));
  166. }
  167. #ifdef RECYCLER_MEMORY_VERIFY
  168. if (this->IsExplicitFreeObjectListAllocMode())
  169. {
  170. HeapBlock* heapBlock = recycler->FindHeapBlock(memBlock);
  171. Assert(heapBlock != nullptr);
  172. Assert(!heapBlock->IsLargeHeapBlock());
  173. TBlockType* smallBlock = (TBlockType*)heapBlock;
  174. smallBlock->ClearExplicitFreeBitForObject(memBlock);
  175. }
  176. #endif
  177. #if DBG || defined(RECYCLER_STATS)
  178. if (!IsExplicitFreeObjectListAllocMode())
  179. {
  180. BOOL isSet = heapBlock->GetDebugFreeBitVector()->TestAndClear(heapBlock->GetAddressBitIndex(memBlock));
  181. Assert(isSet);
  182. }
  183. #endif
  184. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  185. // If we are allocating during concurrent sweep we must mark the object to prevent it from being swept
  186. // in the ongoing sweep.
  187. if (heapBlock != nullptr && heapBlock->isPendingConcurrentSweepPrep)
  188. {
  189. AssertMsg(!this->isAllocatingFromNewBlock, "We shouldn't be tracking allocation to a new block; i.e. bump allocation; during concurrent sweep.");
  190. AssertMsg(!heapBlock->IsAnyFinalizableBlock(), "Allocations are not allowed to finalizable blocks during concurrent sweep.");
  191. AssertMsg(heapBlock->heapBucket->AllocationsStartedDuringConcurrentSweep(), "We shouldn't be allocating from this block while allocations are disabled.");
  192. // Explcitly mark this object and also clear the free bit.
  193. heapBlock->SetObjectMarkedBit(memBlock);
  194. #if DBG || defined(RECYCLER_SLOW_CHECK_ENABLED)
  195. uint bitIndex = heapBlock->GetAddressBitIndex(memBlock);
  196. heapBlock->GetDebugFreeBitVector()->Clear(bitIndex);
  197. heapBlock->objectsMarkedDuringSweep++;
  198. #endif
  199. #ifdef RECYCLER_TRACE
  200. if (recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::ConcurrentSweepPhase) && recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::MemoryAllocationPhase) && CONFIG_FLAG_RELEASE(Verbose))
  201. {
  202. Output::Print(_u("[**33**]FreeListAlloc: Object 0x%p from HeapBlock 0x%p used for allocation during ConcurrentSweep [CollectionState: %d] \n"), memBlock, heapBlock, static_cast<CollectionState>(recycler->collectionState));
  203. }
  204. #endif
  205. }
  206. #endif
  207. return memBlock;
  208. }
  209. return nullptr;
  210. }
  211. template <typename TBlockType>
  212. template <ObjectInfoBits attributes>
  213. inline char *
  214. SmallHeapBlockAllocator<TBlockType>::InlinedAlloc(Recycler * recycler, DECLSPEC_GUARD_OVERFLOW size_t sizeCat)
  215. {
  216. return InlinedAllocImpl<true /* allow fault injection */>(recycler, sizeCat, attributes);
  217. }
  218. template <typename TBlockType>
  219. template <bool canFaultInject>
  220. inline
  221. char *
  222. SmallHeapBlockAllocator<TBlockType>::SlowAlloc(Recycler * recycler, DECLSPEC_GUARD_OVERFLOW size_t sizeCat, ObjectInfoBits attributes)
  223. {
  224. Assert((attributes & InternalObjectInfoBitMask) == attributes);
  225. return InlinedAllocImpl<canFaultInject>(recycler, sizeCat, attributes);
  226. }
  227. }