void OsmAnd::FavoriteLocationsPresenter_P::syncFavoriteLocationMarkers()
{
    QWriteLocker scopedLocker(&_favoriteLocationToMarkerMapLock);

    const auto favoriteLocations = copyAs< QList< std::shared_ptr<const IFavoriteLocation> > >(
        owner->collection->getFavoriteLocations());

    // Remove all markers that have no corresponding favorite locations anymore
    auto itObsoleteEntry = mutableIteratorOf(_favoriteLocationToMarkerMap);
    while (itObsoleteEntry.hasNext())
    {
        const auto& obsoleteEntry = itObsoleteEntry.next();

        if (favoriteLocations.contains(obsoleteEntry.key()))
            continue;
         
        _markersCollection->removeMarker(obsoleteEntry.value());
        itObsoleteEntry.remove();
    }

    // Create markers for all new favorite locations
    MapMarkerBuilder markerBuilder;
    markerBuilder.setBaseOrder(0);
    markerBuilder.setIsAccuracyCircleSupported(false);
    markerBuilder.setPinIcon(
        owner->favoriteLocationPinIconBitmap
        ? owner->favoriteLocationPinIconBitmap
        : FavoriteLocationsPresenter::getDefaultFavoriteLocationPinIconBitmap());
    markerBuilder.setPinIconAlignment(
        owner->favoriteLocationPinIconAlignment.isSet()
        ? *owner->favoriteLocationPinIconAlignment.getValuePtrOrNullptr()
        : FavoriteLocationsPresenter::getDefaultFavoriteLocationPinIconAlignment());
    for (const auto& favoriteLocation : favoriteLocations)
    {
        if (_favoriteLocationToMarkerMap.contains(favoriteLocation))
            continue;

        markerBuilder.setPosition(favoriteLocation->getPosition31());
        markerBuilder.setPinIconModulationColor(favoriteLocation->getColor());
        markerBuilder.setIsHidden(favoriteLocation->isHidden());

        const auto marker = markerBuilder.buildAndAddToCollection(_markersCollection);
        _favoriteLocationToMarkerMap.insert(favoriteLocation, marker);
    }
}
void OsmAnd::CachingRoadLocator_P::clearCacheConditional(const std::function<bool (const std::shared_ptr<const ObfRoutingSectionReader::DataBlock>& dataBlock)> shouldRemoveFromCacheFunctor)
{
    QMutexLocker scopedLocker(&_referencedDataBlocksMapMutex);

    auto itReferencedDataBlocks = mutableIteratorOf(_referencedDataBlocksMap);
    while (itReferencedDataBlocks.hasNext())
    {
        auto& referencedDataBlocks = itReferencedDataBlocks.next().value();

        if (!shouldRemoveFromCacheFunctor(referencedDataBlocks.first()))
            continue;

        for (auto& reference : referencedDataBlocks)
        {
            if (shouldRemoveFromCacheFunctor(reference))
                _cache.releaseReference(reference->id, reference);
        }
        itReferencedDataBlocks.remove();
    }
}
void OsmAnd::ObfsCollection_P::collectSources() const
{
    QWriteLocker scopedLocker1(&_collectedSourcesLock);
    QReadLocker scopedLocker2(&_sourcesOriginsLock);

    // Capture how many invalidations are going to be processed
    const auto invalidationsToProcess = _collectedSourcesInvalidated.loadAcquire();
    if (invalidationsToProcess == 0)
        return;

#if OSMAND_DEBUG
    const auto collectSources_Begin = std::chrono::high_resolution_clock::now();
#endif

    // Check all previously collected sources
    auto itCollectedSourcesEntry = mutableIteratorOf(_collectedSources);
    while(itCollectedSourcesEntry.hasNext())
    {
        const auto& collectedSourcesEntry = itCollectedSourcesEntry.next();
        const auto& originId = collectedSourcesEntry.key();
        auto& collectedSources = collectedSourcesEntry.value();
        const auto& itSourceOrigin = _sourcesOrigins.constFind(originId);

        // If current source origin was removed,
        // remove entire each collected source related to it
        if (itSourceOrigin == _sourcesOrigins.cend())
        {
            // Ensure that ObfFile is not being read anywhere
            for(const auto& itCollectedSource : rangeOf(collectedSources))
            {
                const auto obfFile = itCollectedSource.value();

                //NOTE: OBF should have been locked here, but since file is gone anyways, this lock is quite useless

                itCollectedSource.value().reset();
                assert(obfFile.use_count() == 1);
            }

            itCollectedSourcesEntry.remove();
            continue;
        }

        // Check for missing files
        auto itObfFileEntry = mutableIteratorOf(collectedSources);
        while(itObfFileEntry.hasNext())
        {
            const auto& sourceFilename = itObfFileEntry.next().key();
            if (QFile::exists(sourceFilename))
                continue;
            const auto obfFile = itObfFileEntry.value();

            //NOTE: OBF should have been locked here, but since file is gone anyways, this lock is quite useless

            itObfFileEntry.remove();
            assert(obfFile.use_count() == 1);
        }

        // If all collected sources for current source origin are gone,
        // remove entire collection attached to source origin ID
        if (collectedSources.isEmpty())
        {
            itCollectedSourcesEntry.remove();
            continue;
        }
    }

    // Find all files uncollected sources
    for(const auto& itEntry : rangeOf(constOf(_sourcesOrigins)))
    {
        const auto& originId = itEntry.key();
        const auto& entry = itEntry.value();
        auto itCollectedSources = _collectedSources.find(originId);
        if (itCollectedSources == _collectedSources.end())
            itCollectedSources = _collectedSources.insert(originId, QHash<QString, std::shared_ptr<ObfFile> >());
        auto& collectedSources = *itCollectedSources;

        if (entry->type == SourceOriginType::Directory)
        {
            const auto& directoryAsSourceOrigin = std::static_pointer_cast<const DirectoryAsSourceOrigin>(entry);

            QFileInfoList obfFilesInfo;
            Utilities::findFiles(directoryAsSourceOrigin->directory, QStringList() << QLatin1String("*.obf"), obfFilesInfo, directoryAsSourceOrigin->isRecursive);
            for(const auto& obfFileInfo : constOf(obfFilesInfo))
            {
                const auto& obfFilePath = obfFileInfo.canonicalFilePath();
                if (collectedSources.constFind(obfFilePath) != collectedSources.cend())
                    continue;
                
                auto obfFile = new ObfFile(obfFilePath, obfFileInfo.size());
                collectedSources.insert(obfFilePath, std::shared_ptr<ObfFile>(obfFile));
            }

            if (directoryAsSourceOrigin->isRecursive)
            {
                QFileInfoList directoriesInfo;
                Utilities::findDirectories(directoryAsSourceOrigin->directory, QStringList() << QLatin1String("*"), directoriesInfo, true);

                for(const auto& directoryInfo : constOf(directoriesInfo))
                {
                    const auto canonicalPath = directoryInfo.canonicalFilePath();
                    if (directoryAsSourceOrigin->watchedSubdirectories.contains(canonicalPath))
                        continue;

                    _fileSystemWatcher->addPath(canonicalPath);
                    directoryAsSourceOrigin->watchedSubdirectories.insert(canonicalPath);
                }
            }
        }
        else if (entry->type == SourceOriginType::File)
        {
            const auto& fileAsSourceOrigin = std::static_pointer_cast<const FileAsSourceOrigin>(entry);

            if (!fileAsSourceOrigin->fileInfo.exists())
                continue;
            const auto& obfFilePath = fileAsSourceOrigin->fileInfo.canonicalFilePath();
            if (collectedSources.constFind(obfFilePath) != collectedSources.cend())
                continue;

            auto obfFile = new ObfFile(obfFilePath, fileAsSourceOrigin->fileInfo.size());
            collectedSources.insert(obfFilePath, std::shared_ptr<ObfFile>(obfFile));
        }
    }

    // Decrement invalidations counter with number of processed onces
    _collectedSourcesInvalidated.fetchAndAddOrdered(-invalidationsToProcess);

#if OSMAND_DEBUG
    const auto collectSources_End = std::chrono::high_resolution_clock::now();
    const std::chrono::duration<float> collectSources_Elapsed = collectSources_End - collectSources_Begin;
    LogPrintf(LogSeverityLevel::Info, "Collected OBF sources in %fs", collectSources_Elapsed.count());
#endif
}