#include #include #include #include namespace mbgl { void TileCache::setSize(size_t size_) { MLN_TRACE_FUNC(); size = size_; while (orderedKeys.size() > size) { const auto key = orderedKeys.front(); orderedKeys.remove(key); auto hit = tiles.find(key); if (hit != tiles.end()) { auto tile = std::move(hit->second); tiles.erase(hit); deferredRelease(std::move(tile)); } } assert(orderedKeys.size() <= size); } namespace { /// This exists solely to prevent a problem where temporary lambda captures /// are retained for the duration of the scope instead of being destroyed immediately. template struct CaptureWrapper { CaptureWrapper(std::unique_ptr&& item_) : item(std::move(item_)) {} CaptureWrapper(const CaptureWrapper& other) : item(other.item) {} std::shared_ptr item; }; } // namespace void TileCache::deferredRelease(std::unique_ptr&& tile) { MLN_TRACE_FUNC(); tile->cancel(); // The `std::function` must be created in a separate statement from the `schedule` call. // Creating a `std::function` from a lambda involves a copy, which is why we must use // `shared_ptr` rather than `unique_ptr` for the capture. As a result, a temporary holds // a reference until the construction is complete and the lambda is destroyed. // If this temporary outlives the `schedule` call, and the function is executed immediately // by a waiting thread and is already complete, that temporary reference ends up being the // last one and the destruction actually occurs here on this thread. std::function func{[tile_{CaptureWrapper{std::move(tile)}}, this]() mutable { tile_.item = {}; std::lock_guard counterLock(deferredSignalLock); deferredDeletionsPending--; deferredSignal.notify_all(); }}; std::unique_lock counterLock(deferredSignalLock); deferredDeletionsPending++; threadPool.schedule(std::move(func)); } void TileCache::add(const OverscaledTileID& key, std::unique_ptr&& tile) { MLN_TRACE_FUNC(); if (!tile->isRenderable() || !size) { deferredRelease(std::move(tile)); return; } const auto result = tiles.insert(std::make_pair(key, std::unique_ptr{})); if (result.second) { // inserted result.first->second = std::move(tile); } else { // already present // remove existing tile key to move it to the end orderedKeys.remove(key); // release the newly-provided item deferredRelease(std::move(tile)); } // (re-)insert tile key as newest orderedKeys.push_back(key); // purge oldest key/tile if necessary if (orderedKeys.size() > size) { deferredRelease(pop(orderedKeys.front())); } assert(orderedKeys.size() <= size); } Tile* TileCache::get(const OverscaledTileID& key) { auto it = tiles.find(key); if (it != tiles.end()) { return it->second.get(); } else { return nullptr; } } std::unique_ptr TileCache::pop(const OverscaledTileID& key) { std::unique_ptr tile; const auto it = tiles.find(key); if (it != tiles.end()) { tile = std::move(tiles.extract(it).mapped()); orderedKeys.remove(key); assert(tile->isRenderable()); } return tile; } bool TileCache::has(const OverscaledTileID& key) { return tiles.find(key) != tiles.end(); } void TileCache::clear() { for (auto& item : tiles) { deferredRelease(std::move(item.second)); } orderedKeys.clear(); tiles.clear(); } } // namespace mbgl