SmallFinalizableHeapBlock.cpp 26 KB

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