Skip to content

Commit

Permalink
Doubly linked list for deletion candidates
Browse files Browse the repository at this point in the history
  • Loading branch information
azrogers committed Oct 11, 2024
1 parent 38b4811 commit b220031
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 20 deletions.
8 changes: 6 additions & 2 deletions CesiumAsync/include/CesiumAsync/SharedAsset.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <CesiumAsync/SharedAssetDepot.h>
#include <CesiumUtility/DoublyLinkedList.h>
#include <CesiumUtility/ExtensibleObject.h>
#include <CesiumUtility/IntrusivePointer.h>

Expand Down Expand Up @@ -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<const T*>(this));
this->_pDepot->unmarkDeletionCandidate(
*const_cast<SharedAsset<T>*>(this));
}
}

Expand All @@ -105,7 +107,7 @@ class CESIUMASYNC_API SharedAsset : public CesiumUtility::ExtensibleObject {
SharedAssetDepot<T>* pDepot = this->_pDepot;
if (pDepot) {
// Let the depot manage this object's lifetime.
pDepot->markDeletionCandidate(static_cast<const T*>(this));
pDepot->markDeletionCandidate(*const_cast<SharedAsset<T>*>(this));
} else {
// No depot, so destroy this object directly.
delete static_cast<const T*>(this);
Expand Down Expand Up @@ -141,6 +143,8 @@ class CESIUMASYNC_API SharedAsset : public CesiumUtility::ExtensibleObject {
SharedAssetDepot<T>* _pDepot{nullptr};
std::string _uniqueAssetId{};

CesiumUtility::DoublyLinkedListPointers<SharedAsset<T>> _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};
Expand Down
41 changes: 23 additions & 18 deletions CesiumAsync/include/CesiumAsync/SharedAssetDepot.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <CesiumAsync/Future.h>
#include <CesiumAsync/IAssetAccessor.h>
#include <CesiumAsync/IAssetResponse.h>
#include <CesiumUtility/DoublyLinkedList.h>
#include <CesiumUtility/IntrusivePointer.h>
#include <CesiumUtility/ReferenceCounted.h>

Expand Down Expand Up @@ -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<AssetType>& asset) {
std::lock_guard lock(this->deletionCandidatesMutex);

AssetType* pMutableAsset = const_cast<AssetType*>(pAsset);
pMutableAsset->_sizeInDepot = pMutableAsset->getSizeBytes();
this->totalDeletionCandidateMemoryUsage += pMutableAsset->_sizeInDepot;
asset._sizeInDepot = static_cast<AssetType&>(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<AssetType>* pOldAsset = this->deletionCandidates.head();
this->deletionCandidates.remove(*pOldAsset);

this->totalDeletionCandidateMemoryUsage -= pOldAsset->_sizeInDepot;

Expand All @@ -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<AssetType>& 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);
}
}

Expand All @@ -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<AssetType*> deletionCandidates;
CesiumUtility::DoublyLinkedList<
SharedAsset<AssetType>,
&SharedAsset<AssetType>::_deletionListPointers>
deletionCandidates;
// The total amount of memory used by all assets in the deletionCandidates
// list.
std::atomic<int64_t> totalDeletionCandidateMemoryUsage;
Expand Down
11 changes: 11 additions & 0 deletions CesiumUtility/include/CesiumUtility/DoublyLinkedList.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ template <class T> 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,
Expand Down

0 comments on commit b220031

Please sign in to comment.