From b220031fb017c7ded1bcbfd754cf5480d91f2cb9 Mon Sep 17 00:00:00 2001 From: Ashley Rogers Date: Fri, 11 Oct 2024 14:31:23 -0400 Subject: [PATCH] Doubly linked list for deletion candidates --- CesiumAsync/include/CesiumAsync/SharedAsset.h | 8 +++- .../include/CesiumAsync/SharedAssetDepot.h | 41 +++++++++++-------- .../include/CesiumUtility/DoublyLinkedList.h | 11 +++++ 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/CesiumAsync/include/CesiumAsync/SharedAsset.h b/CesiumAsync/include/CesiumAsync/SharedAsset.h index 39b03c3e0..2a82e605c 100644 --- a/CesiumAsync/include/CesiumAsync/SharedAsset.h +++ b/CesiumAsync/include/CesiumAsync/SharedAsset.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -88,7 +89,8 @@ class CESIUMASYNC_API SharedAsset : public CesiumUtility::ExtensibleObject { void addReference() const /*noexcept*/ { const int32_t prevReferences = this->_referenceCount++; if (this->_pDepot && prevReferences <= 0) { - this->_pDepot->unmarkDeletionCandidate(static_cast(this)); + this->_pDepot->unmarkDeletionCandidate( + *const_cast*>(this)); } } @@ -105,7 +107,7 @@ class CESIUMASYNC_API SharedAsset : public CesiumUtility::ExtensibleObject { SharedAssetDepot* pDepot = this->_pDepot; if (pDepot) { // Let the depot manage this object's lifetime. - pDepot->markDeletionCandidate(static_cast(this)); + pDepot->markDeletionCandidate(*const_cast*>(this)); } else { // No depot, so destroy this object directly. delete static_cast(this); @@ -141,6 +143,8 @@ class CESIUMASYNC_API SharedAsset : public CesiumUtility::ExtensibleObject { SharedAssetDepot* _pDepot{nullptr}; std::string _uniqueAssetId{}; + CesiumUtility::DoublyLinkedListPointers> _deletionListPointers; + // The size of this asset when it was counted by the depot. This is stored so // that the exact same size can be subtracted later. int64_t _sizeInDepot{0}; diff --git a/CesiumAsync/include/CesiumAsync/SharedAssetDepot.h b/CesiumAsync/include/CesiumAsync/SharedAssetDepot.h index a162bb2ef..3054e0952 100644 --- a/CesiumAsync/include/CesiumAsync/SharedAssetDepot.h +++ b/CesiumAsync/include/CesiumAsync/SharedAssetDepot.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -208,24 +209,23 @@ class CESIUMASYNC_API SharedAssetDepot * Marks the given asset as a candidate for deletion. * Should only be called by {@link SharedAsset}. */ - void markDeletionCandidate(const AssetType* pAsset) { + void markDeletionCandidate(SharedAsset& asset) { std::lock_guard lock(this->deletionCandidatesMutex); - AssetType* pMutableAsset = const_cast(pAsset); - pMutableAsset->_sizeInDepot = pMutableAsset->getSizeBytes(); - this->totalDeletionCandidateMemoryUsage += pMutableAsset->_sizeInDepot; + asset._sizeInDepot = static_cast(asset).getSizeBytes(); + this->totalDeletionCandidateMemoryUsage += asset._sizeInDepot; - this->deletionCandidates.push_back(pMutableAsset); + this->deletionCandidates.insertAtTail(asset); if (this->totalDeletionCandidateMemoryUsage > this->staleAssetSizeLimit) { std::lock_guard assetsLock(this->assetsMutex); // Delete the deletion candidates until we're below the limit. - while (!this->deletionCandidates.empty() && + while (this->deletionCandidates.size() > 0 && this->totalDeletionCandidateMemoryUsage > this->staleAssetSizeLimit) { - const AssetType* pOldAsset = this->deletionCandidates.front(); - this->deletionCandidates.pop_front(); + SharedAsset* pOldAsset = this->deletionCandidates.head(); + this->deletionCandidates.remove(*pOldAsset); this->totalDeletionCandidateMemoryUsage -= pOldAsset->_sizeInDepot; @@ -240,19 +240,21 @@ class CESIUMASYNC_API SharedAssetDepot * Unmarks the given asset as a candidate for deletion. * Should only be called by {@link SharedAsset}. */ - void unmarkDeletionCandidate(const AssetType* pAsset) { + void unmarkDeletionCandidate(SharedAsset& asset) { std::lock_guard lock(this->deletionCandidatesMutex); - auto it = std::find( - this->deletionCandidates.begin(), - this->deletionCandidates.end(), - pAsset); + // We should only be deleting this element if it's in the deletionCandidates + // list. + bool isFound = !asset._deletionListPointers.isOrphan() || + (this->deletionCandidates.size() == 1 && + this->deletionCandidates.head()->_uniqueAssetId == + asset.getUniqueAssetId()); - CESIUM_ASSERT(it != this->deletionCandidates.end()); + CESIUM_ASSERT(isFound); - if (it != this->deletionCandidates.end()) { - this->totalDeletionCandidateMemoryUsage -= (*it)->_sizeInDepot; - this->deletionCandidates.erase(it); + if (isFound) { + this->totalDeletionCandidateMemoryUsage -= asset._sizeInDepot; + this->deletionCandidates.remove(asset); } } @@ -268,7 +270,10 @@ class CESIUMASYNC_API SharedAssetDepot // List of assets that are being considered for deletion, in the order that // they were added. - std::list deletionCandidates; + CesiumUtility::DoublyLinkedList< + SharedAsset, + &SharedAsset::_deletionListPointers> + deletionCandidates; // The total amount of memory used by all assets in the deletionCandidates // list. std::atomic totalDeletionCandidateMemoryUsage; diff --git a/CesiumUtility/include/CesiumUtility/DoublyLinkedList.h b/CesiumUtility/include/CesiumUtility/DoublyLinkedList.h index 4785b7947..8d52b3abc 100644 --- a/CesiumUtility/include/CesiumUtility/DoublyLinkedList.h +++ b/CesiumUtility/include/CesiumUtility/DoublyLinkedList.h @@ -34,6 +34,17 @@ template class DoublyLinkedListPointers final { return *this; } + /** + * @brief Is this item an orphan? + * + * Items are considered orphaned if their `pPrevious` and `pNext` pointers are + * both `nullptr`. This means that the item is either the only item in its + * list, or not in a list at all. + */ + bool isOrphan() const { + return this->pNext == nullptr && this->pPrevious == nullptr; + } + private: template < class TElement,