SmallFinalizableHeapBlock.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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. #include "CommonMemoryPch.h"
  6. #ifdef RECYCLER_WRITE_BARRIER
  7. template <class TBlockAttributes>
  8. SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes>*
  9. SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes>::New(HeapBucketT<SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes>> * bucket)
  10. {
  11. CompileAssert(TBlockAttributes::MaxObjectSize <= USHRT_MAX);
  12. Assert(bucket->sizeCat <= TBlockAttributes::MaxObjectSize);
  13. Assert((TBlockAttributes::PageCount * AutoSystemInfo::PageSize) / bucket->sizeCat <= USHRT_MAX);
  14. ushort objectSize = (ushort)bucket->sizeCat;
  15. ushort objectCount = (ushort)(TBlockAttributes::PageCount * AutoSystemInfo::PageSize) / objectSize;
  16. return NoMemProtectHeapNewNoThrowPlusPrefixZ(Base::GetAllocPlusSize(objectCount), SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes>, bucket, objectSize, objectCount);
  17. }
  18. template <class TBlockAttributes>
  19. void
  20. SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes>::Delete(SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes>* heapBlock)
  21. {
  22. Assert(heapBlock->IsAnyFinalizableBlock());
  23. Assert(heapBlock->IsWithBarrier());
  24. NoMemProtectHeapDeletePlusPrefix(Base::GetAllocPlusSize(heapBlock->objectCount), heapBlock);
  25. }
  26. #endif
  27. #ifdef RECYCLER_VISITED_HOST
  28. template <class TBlockAttributes>
  29. SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>*
  30. SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>::New(HeapBucketT<SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>> * bucket)
  31. {
  32. CompileAssert(TBlockAttributes::MaxObjectSize <= USHRT_MAX);
  33. Assert(bucket->sizeCat <= TBlockAttributes::MaxObjectSize);
  34. Assert((TBlockAttributes::PageCount * AutoSystemInfo::PageSize) / bucket->sizeCat <= USHRT_MAX);
  35. ushort objectSize = (ushort)bucket->sizeCat;
  36. ushort objectCount = (ushort)(TBlockAttributes::PageCount * AutoSystemInfo::PageSize) / objectSize;
  37. return NoMemProtectHeapNewNoThrowPlusPrefixZ(Base::GetAllocPlusSize(objectCount), SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>, bucket, objectSize, objectCount);
  38. }
  39. template <class TBlockAttributes>
  40. void
  41. SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>::Delete(SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>* heapBlock)
  42. {
  43. Assert(heapBlock->IsRecyclerVisitedHostBlock());
  44. NoMemProtectHeapDeletePlusPrefix(Base::GetAllocPlusSize(heapBlock->objectCount), heapBlock);
  45. }
  46. #endif
  47. template <class TBlockAttributes>
  48. SmallFinalizableHeapBlockT<TBlockAttributes> *
  49. SmallFinalizableHeapBlockT<TBlockAttributes>::New(HeapBucketT<SmallFinalizableHeapBlockT<TBlockAttributes>> * bucket)
  50. {
  51. CompileAssert(TBlockAttributes::MaxObjectSize <= USHRT_MAX);
  52. Assert(bucket->sizeCat <= TBlockAttributes::MaxObjectSize);
  53. Assert((TBlockAttributes::PageCount * AutoSystemInfo::PageSize) / bucket->sizeCat <= USHRT_MAX);
  54. ushort objectSize = (ushort)bucket->sizeCat;
  55. ushort objectCount = (ushort)(TBlockAttributes::PageCount * AutoSystemInfo::PageSize) / objectSize;
  56. return NoMemProtectHeapNewNoThrowPlusPrefixZ(Base::GetAllocPlusSize(objectCount), SmallFinalizableHeapBlockT<TBlockAttributes>, bucket, objectSize, objectCount);
  57. }
  58. template <class TBlockAttributes>
  59. void
  60. SmallFinalizableHeapBlockT<TBlockAttributes>::Delete(SmallFinalizableHeapBlockT<TBlockAttributes> * heapBlock)
  61. {
  62. Assert(heapBlock->IsFinalizableBlock());
  63. NoMemProtectHeapDeletePlusPrefix(Base::GetAllocPlusSize(heapBlock->objectCount), heapBlock);
  64. }
  65. template <>
  66. SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>::SmallFinalizableHeapBlockT(HeapBucketT<SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>> * bucket, ushort objectSize, ushort objectCount)
  67. : Base(bucket, objectSize, objectCount, HeapBlock::SmallFinalizableBlockType)
  68. {
  69. // We used AllocZ
  70. Assert(this->finalizeCount == 0);
  71. Assert(this->pendingDisposeCount == 0);
  72. Assert(this->disposedObjectList == nullptr);
  73. Assert(this->disposedObjectListTail == nullptr);
  74. Assert(!this->isPendingDispose);
  75. }
  76. template <>
  77. SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>::SmallFinalizableHeapBlockT(HeapBucketT<SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>> * bucket, ushort objectSize, ushort objectCount)
  78. : Base(bucket, objectSize, objectCount, MediumFinalizableBlockType)
  79. {
  80. // We used AllocZ
  81. Assert(this->finalizeCount == 0);
  82. Assert(this->pendingDisposeCount == 0);
  83. Assert(this->disposedObjectList == nullptr);
  84. Assert(this->disposedObjectListTail == nullptr);
  85. Assert(!this->isPendingDispose);
  86. }
  87. #ifdef RECYCLER_VISITED_HOST
  88. template <class TBlockAttributes>
  89. SmallFinalizableHeapBlockT<TBlockAttributes>::SmallFinalizableHeapBlockT(HeapBucketT<SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>> * bucket, ushort objectSize, ushort objectCount, HeapBlockType blockType)
  90. : SmallNormalHeapBlockT<TBlockAttributes>(bucket, objectSize, objectCount, blockType)
  91. {
  92. // We used AllocZ
  93. Assert(this->finalizeCount == 0);
  94. Assert(this->pendingDisposeCount == 0);
  95. Assert(this->disposedObjectList == nullptr);
  96. Assert(this->disposedObjectListTail == nullptr);
  97. Assert(!this->isPendingDispose);
  98. }
  99. #endif
  100. #ifdef RECYCLER_WRITE_BARRIER
  101. template <class TBlockAttributes>
  102. SmallFinalizableHeapBlockT<TBlockAttributes>::SmallFinalizableHeapBlockT(HeapBucketT<SmallFinalizableWithBarrierHeapBlockT<TBlockAttributes>> * bucket, ushort objectSize, ushort objectCount, HeapBlockType blockType)
  103. : SmallNormalHeapBlockT<TBlockAttributes>(bucket, objectSize, objectCount, blockType)
  104. {
  105. // We used AllocZ
  106. Assert(this->finalizeCount == 0);
  107. Assert(this->pendingDisposeCount == 0);
  108. Assert(this->disposedObjectList == nullptr);
  109. Assert(this->disposedObjectListTail == nullptr);
  110. Assert(!this->isPendingDispose);
  111. }
  112. #endif
  113. template <class TBlockAttributes>
  114. void
  115. SmallFinalizableHeapBlockT<TBlockAttributes>::SetAttributes(void * address, unsigned char attributes)
  116. {
  117. Assert((attributes & FinalizeBit) != 0);
  118. __super::SetAttributes(address, attributes);
  119. finalizeCount++;
  120. #ifdef RECYCLER_FINALIZE_CHECK
  121. HeapInfo * heapInfo = this->heapBucket->heapInfo;
  122. heapInfo->liveFinalizableObjectCount++;
  123. heapInfo->newFinalizableObjectCount++;
  124. #endif
  125. }
  126. #ifdef RECYCLER_VISITED_HOST
  127. template <class TBlockAttributes>
  128. void
  129. SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>::SetAttributes(void * address, unsigned char attributes)
  130. {
  131. // Don't call __super, since that has behavior we don't want (it asserts that FinalizeBit is set
  132. // but recycler visited block allows traced only objects; it also unconditionally bumps the heap info
  133. // live/new finalizable object counts which will become unbalance if FinalizeBit is not set).
  134. // We do want the grandparent class behavior though, which actually sets the ObjectInfo bits.
  135. SmallFinalizableHeapBlockT<TBlockAttributes>::Base::SetAttributes(address, attributes);
  136. #ifdef RECYCLER_FINALIZE_CHECK
  137. if (attributes & FinalizeBit)
  138. {
  139. this->finalizeCount++;
  140. HeapInfo * heapInfo = this->heapBucket->heapInfo;
  141. heapInfo->liveFinalizableObjectCount++;
  142. heapInfo->newFinalizableObjectCount++;
  143. }
  144. #endif
  145. }
  146. template <class TBlockAttributes>
  147. template <bool doSpecialMark>
  148. _NOINLINE
  149. void SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>::ProcessMarkedObject(void* objectAddress, MarkContext * markContext)
  150. {
  151. unsigned char * attributes = nullptr;
  152. if (!this->TryGetAddressOfAttributes(objectAddress, &attributes))
  153. {
  154. return;
  155. }
  156. if (!this->template UpdateAttributesOfMarkedObjects<doSpecialMark>(markContext, objectAddress, this->objectSize, *attributes,
  157. [&](unsigned char _attributes) { *attributes = _attributes; }))
  158. {
  159. // Couldn't mark children- bail out and come back later
  160. this->SetNeedOOMRescan(markContext->GetRecycler());
  161. }
  162. }
  163. template <class TBlockAttributes>
  164. template <bool doSpecialMark, typename Fn>
  165. bool SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>::UpdateAttributesOfMarkedObjects(MarkContext * markContext, void * objectAddress, size_t objectSize, unsigned char attributes, Fn fn)
  166. {
  167. bool noOOMDuringMark = true;
  168. if (attributes & TrackBit)
  169. {
  170. Assert((attributes & LeafBit) == 0);
  171. IRecyclerVisitedObject* recyclerVisited = static_cast<IRecyclerVisitedObject*>(objectAddress);
  172. noOOMDuringMark = markContext->AddPreciselyTracedObject(recyclerVisited);
  173. if (noOOMDuringMark)
  174. {
  175. // Object has been successfully processed, so clear NewTrackBit
  176. attributes &= ~NewTrackBit;
  177. }
  178. else
  179. {
  180. // Set the NewTrackBit, so that the main thread will redo tracking
  181. attributes |= NewTrackBit;
  182. noOOMDuringMark = false;
  183. }
  184. fn(attributes);
  185. }
  186. #ifdef RECYCLER_STATS
  187. RECYCLER_STATS_INTERLOCKED_INC(markContext->GetRecycler(), markData.markCount);
  188. RECYCLER_STATS_INTERLOCKED_ADD(markContext->GetRecycler(), markData.markBytes, objectSize);
  189. // Count track or finalize if we don't have to process it in thread because of OOM.
  190. if ((attributes & (TrackBit | NewTrackBit)) != (TrackBit | NewTrackBit))
  191. {
  192. // Only count those we have queued, so we don't double count
  193. if (attributes & TrackBit)
  194. {
  195. RECYCLER_STATS_INTERLOCKED_INC(markContext->GetRecycler(), trackCount);
  196. }
  197. if (attributes & FinalizeBit)
  198. {
  199. // we counted the finalizable object here,
  200. // turn off the new bit so we don't count it again
  201. // on Rescan
  202. attributes &= ~NewFinalizeBit;
  203. fn(attributes);
  204. RECYCLER_STATS_INTERLOCKED_INC(markContext->GetRecycler(), finalizeCount);
  205. }
  206. }
  207. #endif
  208. return noOOMDuringMark;
  209. }
  210. // static
  211. template <class TBlockAttributes>
  212. bool
  213. SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes>::RescanObject(SmallRecyclerVisitedHostHeapBlockT<TBlockAttributes> * block, __in_ecount(localObjectSize) char * objectAddress, uint localObjectSize,
  214. uint objectIndex, Recycler * recycler)
  215. {
  216. unsigned char const attributes = block->ObjectInfo(objectIndex);
  217. if ((attributes & TrackBit) != 0)
  218. {
  219. Assert((attributes & LeafBit) == 0);
  220. Assert(block->GetAddressIndex(objectAddress) != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit);
  221. if (!recycler->AddPreciselyTracedMark(reinterpret_cast<IRecyclerVisitedObject*>(objectAddress)))
  222. {
  223. // Failed to add to the mark stack due to OOM.
  224. return false;
  225. }
  226. // We have processed this object as tracked, we can clear the NewTrackBit
  227. block->ObjectInfo(objectIndex) &= ~NewTrackBit;
  228. RECYCLER_STATS_INC(recycler, trackCount);
  229. RECYCLER_STATS_INC(recycler, markData.rescanObjectCount);
  230. RECYCLER_STATS_ADD(recycler, markData.rescanObjectByteCount, localObjectSize);
  231. }
  232. #ifdef RECYCLER_STATS
  233. if (attributes & FinalizeBit)
  234. {
  235. // Concurrent thread mark the object before the attribute is set and missed the finalize count
  236. // For finalized object, we will always write a dummy vtable before returning to the call,
  237. // so the page will always need to be rescanned, and we can count those here.
  238. // NewFinalizeBit is cleared if the background thread has already counted the object.
  239. // So if it is still set here, we need to count it
  240. RECYCLER_STATS_INC_IF(attributes & NewFinalizeBit, recycler, finalizeCount);
  241. block->ObjectInfo(objectIndex) &= ~NewFinalizeBit;
  242. }
  243. #endif
  244. return true;
  245. }
  246. #endif
  247. template <class TBlockAttributes>
  248. bool
  249. SmallFinalizableHeapBlockT<TBlockAttributes>::TryGetAttributes(void* objectAddress, unsigned char * pAttr)
  250. {
  251. unsigned char * attributes = nullptr;
  252. if (this->TryGetAddressOfAttributes(objectAddress, &attributes))
  253. {
  254. *pAttr = *attributes;
  255. return true;
  256. }
  257. return false;
  258. }
  259. template <class TBlockAttributes>
  260. bool
  261. SmallFinalizableHeapBlockT<TBlockAttributes>::TryGetAddressOfAttributes(void* objectAddress, unsigned char ** ppAttrs)
  262. {
  263. ushort objectIndex = this->GetAddressIndex(objectAddress);
  264. if (objectIndex == SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit)
  265. {
  266. // Not a valid offset within the block. No further processing necessary.
  267. return false;
  268. }
  269. *ppAttrs = &this->ObjectInfo(objectIndex);
  270. return true;
  271. }
  272. template <class TBlockAttributes>
  273. template <bool doSpecialMark>
  274. _NOINLINE
  275. void
  276. SmallFinalizableHeapBlockT<TBlockAttributes>::ProcessMarkedObject(void* objectAddress, MarkContext * markContext)
  277. {
  278. unsigned char * attributes = nullptr;
  279. if (!this->TryGetAddressOfAttributes(objectAddress, &attributes))
  280. {
  281. return;
  282. }
  283. if (!this->template UpdateAttributesOfMarkedObjects<doSpecialMark>(markContext, objectAddress, this->objectSize, *attributes,
  284. [&](unsigned char _attributes) { *attributes = _attributes; }))
  285. {
  286. // Couldn't mark children- bail out and come back later
  287. this->SetNeedOOMRescan(markContext->GetRecycler());
  288. }
  289. }
  290. #if ENABLE_PARTIAL_GC || ENABLE_CONCURRENT_GC
  291. // static
  292. template <class TBlockAttributes>
  293. bool
  294. SmallFinalizableHeapBlockT<TBlockAttributes>::CanRescanFullBlock()
  295. {
  296. // Finalizable block need to rescan object one at a time.
  297. return false;
  298. }
  299. // static
  300. template <class TBlockAttributes>
  301. bool
  302. SmallFinalizableHeapBlockT<TBlockAttributes>::RescanObject(SmallFinalizableHeapBlockT<TBlockAttributes> * block, __in_ecount(localObjectSize) char * objectAddress, uint localObjectSize,
  303. uint objectIndex, Recycler * recycler)
  304. {
  305. unsigned char const attributes = block->ObjectInfo(objectIndex);
  306. Assert(block->IsAnyFinalizableBlock());
  307. if ((attributes & LeafBit) == 0)
  308. {
  309. Assert(block->GetAddressIndex(objectAddress) != SmallHeapBlockT<TBlockAttributes>::InvalidAddressBit);
  310. if (!recycler->AddMark(objectAddress, localObjectSize))
  311. {
  312. // Failed to add to the mark stack due to OOM.
  313. return false;
  314. }
  315. RECYCLER_STATS_INC(recycler, markData.rescanObjectCount);
  316. RECYCLER_STATS_ADD(recycler, markData.rescanObjectByteCount, localObjectSize);
  317. }
  318. // Since we mark through unallocated objects, we might have marked an object before it
  319. // is allocated as a tracked object. The object will not be queue up in the
  320. // tracked object list, and NewTrackBit will still be on. Queue it up now.
  321. // NewTrackBit will also be on for tracked object that we weren't able to queue
  322. // because of OOM. In those case, the page is forced to be rescan, and we will
  323. // try to process those again here.
  324. if ((attributes & (TrackBit | NewTrackBit)) == (TrackBit | NewTrackBit))
  325. {
  326. if (!block->RescanTrackedObject((FinalizableObject*) objectAddress, objectIndex, recycler))
  327. {
  328. // Failed to add to the mark stack due to OOM.
  329. return false;
  330. }
  331. }
  332. #ifdef RECYCLER_STATS
  333. else if (attributes & FinalizeBit)
  334. {
  335. // Concurrent thread mark the object before the attribute is set and missed the finalize count
  336. // For finalized object, we will always write a dummy vtable before returning to the call,
  337. // so the page will always need to be rescanned, and we can count those here.
  338. // NewFinalizeBit is cleared if the background thread has already counted the object.
  339. // So if it is still set here, we need to count it
  340. RECYCLER_STATS_INC_IF(attributes & NewFinalizeBit, recycler, finalizeCount);
  341. block->ObjectInfo(objectIndex) &= ~NewFinalizeBit;
  342. }
  343. #endif
  344. return true;
  345. }
  346. template <class TBlockAttributes>
  347. bool
  348. SmallFinalizableHeapBlockT<TBlockAttributes>::RescanTrackedObject(FinalizableObject * object, uint objectIndex, Recycler * recycler)
  349. {
  350. RecyclerVerboseTrace(recycler->GetRecyclerFlagsTable(), _u("Marking 0x%08x during rescan\n"), object);
  351. #if ENABLE_CONCURRENT_GC
  352. #if ENABLE_PARTIAL_GC
  353. if (recycler->inPartialCollectMode)
  354. {
  355. Assert(!recycler->DoQueueTrackedObject());
  356. }
  357. else
  358. #endif
  359. {
  360. Assert(recycler->DoQueueTrackedObject());
  361. if (!recycler->QueueTrackedObject(object))
  362. {
  363. // Failed to add to track stack due to OOM.
  364. return false;
  365. }
  366. }
  367. RECYCLER_STATS_INC(recycler, trackCount);
  368. RECYCLER_STATS_INC_IF(this->ObjectInfo(objectIndex) & FinalizeBit, recycler, finalizeCount);
  369. // We have processed this object as tracked, we can clear the NewTrackBit
  370. this->ObjectInfo(objectIndex) &= ~NewTrackBit;
  371. return true;
  372. #else
  373. // REVIEW: Is this correct? Or should we remove the track bit always?
  374. return false;
  375. #endif
  376. }
  377. #endif
  378. template <class TBlockAttributes>
  379. SweepState
  380. SmallFinalizableHeapBlockT<TBlockAttributes>::Sweep(RecyclerSweep& recyclerSweep, bool queuePendingSweep, bool allocable)
  381. {
  382. Assert(!recyclerSweep.IsBackground());
  383. Assert(!queuePendingSweep);
  384. // If there are finalizable objects in this heap block, they need to be swept
  385. // in-thread and not in the concurrent thread, so don't queue pending sweep
  386. return SmallNormalHeapBlockT<TBlockAttributes>::Sweep(recyclerSweep, false, allocable, this->finalizeCount, HasAnyDisposeObjects());
  387. }
  388. template <class TBlockAttributes>
  389. void
  390. SmallFinalizableHeapBlockT<TBlockAttributes>::DisposeObjects()
  391. {
  392. Assert(this->isPendingDispose);
  393. Assert(HasAnyDisposeObjects());
  394. // PARTIALGC-CONSIDER: page with finalizable/disposable object will always be modified
  395. // because calling dispose probably will modify object itself, and it may call other
  396. // script that might touch the page as well. We can't distinguish between these two kind
  397. // of write to the page.
  398. //
  399. // Possible mitigation include:
  400. // - allocating finalizable/disposable object on separate pages
  401. // - some of the object only need finalize, but not dispose. mark them separately
  402. //
  403. // For now, we always touch the page by zeroing out disposed object which should be moved as well.
  404. ForEachPendingDisposeObject([&] (uint index) {
  405. void * objectAddress = this->address + (this->objectSize * index);
  406. // Dispose the object.
  407. // Note that Dispose can cause reentrancy, which can cause allocation, which can cause collection.
  408. // The object we're disposing is still considered PendingDispose until the Dispose call completes.
  409. // So in case we call CheckFreeBitVector or similar, we should still see correct state re this object.
  410. ((FinalizableObject *)objectAddress)->Dispose(false);
  411. Assert(finalizeCount != 0);
  412. finalizeCount--;
  413. Assert(pendingDisposeCount != 0);
  414. pendingDisposeCount--;
  415. // Properly enqueue the processed object
  416. // This will also clear the ObjectInfo bits so it's not marked as PendingDispose anymore
  417. this->EnqueueProcessedObject(&disposedObjectList, &disposedObjectListTail, objectAddress, index);
  418. RECYCLER_STATS_INC(this->heapBucket->heapInfo->recycler, finalizeSweepCount);
  419. #ifdef RECYCLER_FINALIZE_CHECK
  420. this->heapBucket->heapInfo->liveFinalizableObjectCount--;
  421. this->heapBucket->heapInfo->pendingDisposableObjectCount--;
  422. #endif
  423. });
  424. // Dispose could have re-entered and caused new pending dispose objects on this block.
  425. // If so, recycler->hasDisposableObject will have been set again, and we will do another
  426. // round of Dispose to actually dispose these objects.
  427. Assert(this->pendingDisposeCount == 0 || this->heapBucket->heapInfo->recycler->hasDisposableObject);
  428. }
  429. template <class TBlockAttributes>
  430. void
  431. SmallFinalizableHeapBlockT<TBlockAttributes>::TransferDisposedObjects()
  432. {
  433. // CONCURRENT-TODO: we don't allocate on pending disposed blocks during concurrent sweep or disable dispose
  434. // So the free bit vector must be valid
  435. Assert(this->IsFreeBitsValid());
  436. Assert(this->isPendingDispose);
  437. Assert(this->pendingDisposeCount == 0);
  438. DebugOnly(this->isPendingDispose = false);
  439. this->TransferProcessedObjects(this->disposedObjectList, this->disposedObjectListTail);
  440. this->disposedObjectList = nullptr;
  441. this->disposedObjectListTail = nullptr;
  442. // We already updated the bit vector on TransferSweptObjects
  443. // So just update the free object head.
  444. this->lastFreeObjectHead = this->freeObjectList;
  445. RECYCLER_SLOW_CHECK(this->CheckFreeBitVector(true));
  446. }
  447. template <class TBlockAttributes>
  448. ushort
  449. SmallFinalizableHeapBlockT<TBlockAttributes>::AddDisposedObjectFreeBitVector(SmallHeapBlockBitVector * free)
  450. {
  451. // all the finalized object are considered freed, but not allocable yet
  452. ushort freeCount = 0;
  453. FreeObject * freeObject = this->disposedObjectList;
  454. if (freeObject != nullptr)
  455. {
  456. while (true)
  457. {
  458. uint bitIndex = this->GetAddressBitIndex(freeObject);
  459. Assert(this->IsValidBitIndex(bitIndex));
  460. // not allocable yet
  461. Assert(!this->GetDebugFreeBitVector()->Test(bitIndex));
  462. // but in the free list to mark can skip scanning the object
  463. free->Set(bitIndex);
  464. freeCount++;
  465. if (freeObject == this->disposedObjectListTail)
  466. {
  467. break;
  468. }
  469. freeObject = freeObject->GetNext();
  470. }
  471. }
  472. return freeCount;
  473. }
  474. template <class TBlockAttributes>
  475. void
  476. SmallFinalizableHeapBlockT<TBlockAttributes>::FinalizeAllObjects()
  477. {
  478. if (this->finalizeCount != 0)
  479. {
  480. DebugOnly(uint processedCount = 0);
  481. this->ForEachAllocatedObject(FinalizeBit, [&](uint index, void * objectAddress)
  482. {
  483. FinalizableObject * finalizableObject = ((FinalizableObject *)objectAddress);
  484. finalizableObject->Finalize(true);
  485. finalizableObject->Dispose(true);
  486. #ifdef RECYCLER_FINALIZE_CHECK
  487. this->heapBucket->heapInfo->liveFinalizableObjectCount --;
  488. #endif
  489. DebugOnly(processedCount++);
  490. });
  491. this->ForEachPendingDisposeObject([&] (uint index) {
  492. void * objectAddress = this->address + (this->objectSize * index);
  493. ((FinalizableObject *)objectAddress)->Dispose(true);
  494. #ifdef RECYCLER_FINALIZE_CHECK
  495. this->heapBucket->heapInfo->liveFinalizableObjectCount--;
  496. this->heapBucket->heapInfo->pendingDisposableObjectCount--;
  497. #endif
  498. DebugOnly(processedCount++);
  499. });
  500. Assert(this->finalizeCount == processedCount);
  501. }
  502. }
  503. #if DBG
  504. template <class TBlockAttributes>
  505. void
  506. SmallFinalizableHeapBlockT<TBlockAttributes>::Init(ushort objectSize, ushort objectCount)
  507. {
  508. Assert(this->disposedObjectList == nullptr);
  509. Assert(this->disposedObjectListTail == nullptr);
  510. Assert(this->finalizeCount == 0);
  511. Assert(this->pendingDisposeCount == 0);
  512. __super::Init(objectSize, objectCount);
  513. }
  514. #if ENABLE_PARTIAL_GC
  515. template <class TBlockAttributes>
  516. void
  517. SmallFinalizableHeapBlockT<TBlockAttributes>::FinishPartialCollect()
  518. {
  519. Assert(this->disposedObjectList == nullptr);
  520. Assert(this->disposedObjectListTail == nullptr);
  521. __super::FinishPartialCollect();
  522. }
  523. #endif
  524. #endif
  525. #ifdef RECYCLER_SLOW_CHECK_ENABLED
  526. template <class TBlockAttributes>
  527. uint
  528. SmallFinalizableHeapBlockT<TBlockAttributes>::CheckDisposedObjectFreeBitVector()
  529. {
  530. uint verifyFreeCount = 0;
  531. // all the finalized object are considered freed, but not allocable yet
  532. FreeObject *freeObject = this->disposedObjectList;
  533. if (freeObject != nullptr)
  534. {
  535. SmallHeapBlockBitVector * free = this->GetFreeBitVector();
  536. while (true)
  537. {
  538. uint bitIndex = this->GetAddressBitIndex(freeObject);
  539. Assert(this->IsValidBitIndex(bitIndex));
  540. Assert(!this->GetDebugFreeBitVector()->Test(bitIndex));
  541. Assert(free->Test(bitIndex));
  542. verifyFreeCount++;
  543. if (freeObject == this->disposedObjectListTail)
  544. {
  545. break;
  546. }
  547. freeObject = freeObject->GetNext();
  548. }
  549. }
  550. return verifyFreeCount;
  551. }
  552. template <class TBlockAttributes>
  553. bool
  554. SmallFinalizableHeapBlockT<TBlockAttributes>::GetFreeObjectListOnAllocator(FreeObject ** freeObjectList)
  555. {
  556. return this->template GetFreeObjectListOnAllocatorImpl<SmallFinalizableHeapBlockT<TBlockAttributes>>(freeObjectList);
  557. }
  558. #endif
  559. namespace Memory
  560. {
  561. template class SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>;
  562. template void SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>::ProcessMarkedObject<true>(void* objectAddress, MarkContext * markContext);
  563. template void SmallFinalizableHeapBlockT<SmallAllocationBlockAttributes>::ProcessMarkedObject<false>(void* objectAddress, MarkContext * markContext);
  564. template class SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>;
  565. template void SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>::ProcessMarkedObject<true>(void* objectAddress, MarkContext * markContext);;
  566. template void SmallFinalizableHeapBlockT<MediumAllocationBlockAttributes>::ProcessMarkedObject<false>(void* objectAddress, MarkContext * markContext);;
  567. #ifdef RECYCLER_VISITED_HOST
  568. template class SmallRecyclerVisitedHostHeapBlockT<SmallAllocationBlockAttributes>;
  569. template void SmallRecyclerVisitedHostHeapBlockT<SmallAllocationBlockAttributes>::ProcessMarkedObject<true>(void* objectAddress, MarkContext * markContext);
  570. template void SmallRecyclerVisitedHostHeapBlockT<SmallAllocationBlockAttributes>::ProcessMarkedObject<false>(void* objectAddress, MarkContext * markContext);
  571. template class SmallRecyclerVisitedHostHeapBlockT<MediumAllocationBlockAttributes>;
  572. template void SmallRecyclerVisitedHostHeapBlockT<MediumAllocationBlockAttributes>::ProcessMarkedObject<true>(void* objectAddress, MarkContext * markContext);;
  573. template void SmallRecyclerVisitedHostHeapBlockT<MediumAllocationBlockAttributes>::ProcessMarkedObject<false>(void* objectAddress, MarkContext * markContext);;
  574. #endif
  575. #ifdef RECYCLER_WRITE_BARRIER
  576. template class SmallFinalizableWithBarrierHeapBlockT<SmallAllocationBlockAttributes>;
  577. template class SmallFinalizableWithBarrierHeapBlockT<MediumAllocationBlockAttributes>;
  578. #endif
  579. }