diff options
author | Alexander Shalamov <alexander.shalamov@mapbox.com> | 2019-10-04 15:02:01 +0300 |
---|---|---|
committer | Alexander Shalamov <alexander.shalamov@mapbox.com> | 2020-01-13 10:57:23 +0200 |
commit | 879c44f661c5eb762c93a721b657859a71aabfc7 (patch) | |
tree | 3a542777434e0d685811ce1c66b752dc9ca36e92 /platform | |
parent | 86a360534994cb37d3dddc53b71a2858d97419c3 (diff) | |
download | qtlocation-mapboxgl-879c44f661c5eb762c93a721b657859a71aabfc7.tar.gz |
[core] Modularize FileSource codebase (#15768)
* [core] Introduce FileSourceManager and use it for default platform impl
- Add `FileSourceManager` interface that provides access to `FileSource`
instances and means of registering / unregistering `FileSource` factories
- Split `DefaultFileSource` into smaller parts
- Add `DatabaseFileSource` interface and it's default implementation
- Remove inter-dependencies between concrete `FileSource` classes
* [build] Add files to next build system
* [core] Add generic property setters / getters
* [core] Remove setOnlineStatus from OnlineFileSource interface
* [core] Hide threading implementation details from DatabaseFileSource interface
* [core] Make DB file source methods virtual
* [core] Add documentation for DatabaseFileSource and rename one method
* [core] Use simple callback instead of ActorRef
* [core] Remove ActorRef from OnlineFileSource public header
* [core] Add callback to FileSource::forward async API
* [core] Pass OfflineRegionDefinition by value
* [core] Update tests to use modular file sources
* [core] Update unit tests
* [core] Update unit tests after rebase
* [core] Backport low prio fix for cached requests
* [core] Backport pack database
* [core] Return removed factory from unRegisterFileSourceFactory
* [core] Rename shadowed args in onlinefilesource
* [core] Remove simple std::function callback aliases
* [core] Expose online file source property keys in public header file
* [test-runner] Add proxy file source test runner
* [cache] Update mbgl-cache utility to use new file source
* [metrics] Rebaseline binary size metrics
* [offline] Update offline utility
* [core] Update changelog
Diffstat (limited to 'platform')
-rw-r--r-- | platform/android/src/test/http_file_source_test_stub.cpp | 2 | ||||
-rw-r--r-- | platform/darwin/filesource-files.json | 1 | ||||
-rw-r--r-- | platform/default/filesource-files.json | 8 | ||||
-rw-r--r-- | platform/default/include/mbgl/storage/offline_download.hpp | 6 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/asset_file_source.cpp | 23 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/database_file_source.cpp | 280 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/default_file_source.cpp | 398 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/file_source.cpp | 15 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/file_source_manager.cpp | 43 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/local_file_source.cpp | 24 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/main_resource_loader.cpp | 223 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/offline_download.cpp | 15 | ||||
-rw-r--r-- | platform/default/src/mbgl/storage/online_file_source.cpp | 369 | ||||
-rw-r--r-- | platform/glfw/main.cpp | 44 | ||||
-rw-r--r-- | platform/linux/filesource-files.json | 1 |
15 files changed, 881 insertions, 571 deletions
diff --git a/platform/android/src/test/http_file_source_test_stub.cpp b/platform/android/src/test/http_file_source_test_stub.cpp index 930a20907a..48e6a965ff 100644 --- a/platform/android/src/test/http_file_source_test_stub.cpp +++ b/platform/android/src/test/http_file_source_test_stub.cpp @@ -1,6 +1,6 @@ #include <mbgl/storage/http_file_source.hpp> #include <mbgl/storage/resource.hpp> - +#include <mbgl/util/async_request.hpp> #include <mbgl/util/async_task.hpp> namespace mbgl { diff --git a/platform/darwin/filesource-files.json b/platform/darwin/filesource-files.json index 62043a0dcd..5d72549f73 100644 --- a/platform/darwin/filesource-files.json +++ b/platform/darwin/filesource-files.json @@ -5,6 +5,7 @@ "platform/darwin/src/MGLNetworkConfiguration.m", "platform/darwin/src/http_file_source.mm", "platform/default/src/mbgl/storage/file_source.cpp", + "platform/default/src/mbgl/storage/file_source_manager.cpp", "platform/default/src/mbgl/storage/sqlite3.cpp" ], "public_headers": {}, diff --git a/platform/default/filesource-files.json b/platform/default/filesource-files.json index f61aa6a335..72e76670b8 100644 --- a/platform/default/filesource-files.json +++ b/platform/default/filesource-files.json @@ -2,19 +2,22 @@ "//": "This file can be edited manually and is the canonical source.", "sources": [ "platform/default/src/mbgl/storage/asset_file_source.cpp", - "platform/default/src/mbgl/storage/default_file_source.cpp", + "platform/default/src/mbgl/storage/database_file_source.cpp", + "platform/default/src/mbgl/storage/file_source_manager.cpp", "platform/default/src/mbgl/storage/file_source_request.cpp", "platform/default/src/mbgl/storage/local_file_request.cpp", "platform/default/src/mbgl/storage/local_file_source.cpp", + "platform/default/src/mbgl/storage/main_resource_loader.cpp", "platform/default/src/mbgl/storage/offline.cpp", "platform/default/src/mbgl/storage/offline_database.cpp", "platform/default/src/mbgl/storage/offline_download.cpp", "platform/default/src/mbgl/storage/online_file_source.cpp" ], "public_headers": { - "mbgl/storage/default_file_source.hpp": "include/mbgl/storage/default_file_source.hpp", + "mbgl/storage/offline_file_source.hpp": "include/mbgl/storage/database_file_source.hpp", "mbgl/storage/offline.hpp": "include/mbgl/storage/offline.hpp", "mbgl/storage/online_file_source.hpp": "include/mbgl/storage/online_file_source.hpp", + "mbgl/storage/file_source_manager.hpp": "include/mbgl/storage/file_source_manager.hpp", "mbgl/storage/file_source_request.hpp": "platform/default/include/mbgl/storage/file_source_request.hpp", "mbgl/storage/local_file_request.hpp": "platform/default/include/mbgl/storage/local_file_request.hpp", "mbgl/storage/merge_sideloaded.hpp": "platform/default/include/mbgl/storage/merge_sideloaded.hpp", @@ -24,6 +27,7 @@ "mbgl/storage/sqlite3.hpp": "platform/default/include/mbgl/storage/sqlite3.hpp" }, "private_headers": { + "mbgl/storage/main_resource_loader.hpp": "src/mbgl/storage/main_resource_loader.hpp", "mbgl/storage/asset_file_source.hpp": "src/mbgl/storage/asset_file_source.hpp", "mbgl/storage/http_file_source.hpp": "src/mbgl/storage/http_file_source.hpp", "mbgl/storage/local_file_source.hpp": "src/mbgl/storage/local_file_source.hpp" diff --git a/platform/default/include/mbgl/storage/offline_download.hpp b/platform/default/include/mbgl/storage/offline_download.hpp index 53b42ae9d1..3a5159470a 100644 --- a/platform/default/include/mbgl/storage/offline_download.hpp +++ b/platform/default/include/mbgl/storage/offline_download.hpp @@ -1,8 +1,8 @@ #pragma once +#include <mbgl/storage/file_source.hpp> #include <mbgl/storage/offline.hpp> #include <mbgl/storage/resource.hpp> -#include <mbgl/storage/online_file_source.hpp> #include <list> #include <unordered_set> @@ -28,7 +28,7 @@ class Parser; */ class OfflineDownload { public: - OfflineDownload(int64_t id, OfflineRegionDefinition&&, OfflineDatabase& offline, OnlineFileSource& online); + OfflineDownload(int64_t id, OfflineRegionDefinition, OfflineDatabase& offline, FileSource& online); ~OfflineDownload(); void setObserver(std::unique_ptr<OfflineRegionObserver>); @@ -53,7 +53,7 @@ private: int64_t id; OfflineRegionDefinition definition; OfflineDatabase& offlineDatabase; - OnlineFileSource& onlineFileSource; + FileSource& onlineFileSource; OfflineRegionStatus status; std::unique_ptr<OfflineRegionObserver> observer; diff --git a/platform/default/src/mbgl/storage/asset_file_source.cpp b/platform/default/src/mbgl/storage/asset_file_source.cpp index b14d73045f..7abd609b19 100644 --- a/platform/default/src/mbgl/storage/asset_file_source.cpp +++ b/platform/default/src/mbgl/storage/asset_file_source.cpp @@ -1,15 +1,17 @@ #include <mbgl/storage/asset_file_source.hpp> #include <mbgl/storage/file_source_request.hpp> #include <mbgl/storage/local_file_request.hpp> +#include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> +#include <mbgl/util/constants.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/thread.hpp> #include <mbgl/util/url.hpp> namespace { - -const std::string assetProtocol = "asset://"; - +bool acceptsURL(const std::string& url) { + return 0 == url.rfind(mbgl::util::ASSET_PROTOCOL, 0); +} } // namespace namespace mbgl { @@ -30,7 +32,8 @@ public: } // Cut off the protocol and prefix with path. - const auto path = root + "/" + mbgl::util::percentDecode(url.substr(assetProtocol.size())); + const auto path = + root + "/" + mbgl::util::percentDecode(url.substr(std::char_traits<char>::length(util::ASSET_PROTOCOL))); requestLocalFile(path, std::move(req)); } @@ -52,8 +55,16 @@ std::unique_ptr<AsyncRequest> AssetFileSource::request(const Resource& resource, return std::move(req); } -bool AssetFileSource::acceptsURL(const std::string& url) { - return 0 == url.rfind(assetProtocol, 0); +bool AssetFileSource::canRequest(const Resource& resource) const { + return acceptsURL(resource.url); +} + +void AssetFileSource::pause() { + impl->pause(); +} + +void AssetFileSource::resume() { + impl->resume(); } } // namespace mbgl diff --git a/platform/default/src/mbgl/storage/database_file_source.cpp b/platform/default/src/mbgl/storage/database_file_source.cpp new file mode 100644 index 0000000000..ba8ba7b31d --- /dev/null +++ b/platform/default/src/mbgl/storage/database_file_source.cpp @@ -0,0 +1,280 @@ +#include <mbgl/storage/database_file_source.hpp> +#include <mbgl/storage/file_source_manager.hpp> +#include <mbgl/storage/file_source_request.hpp> +#include <mbgl/storage/offline_database.hpp> +#include <mbgl/storage/offline_download.hpp> +#include <mbgl/storage/resource_options.hpp> +#include <mbgl/storage/response.hpp> +#include <mbgl/util/constants.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/thread.hpp> + +namespace mbgl { + +// For testing use only +constexpr const char* READ_ONLY_MODE_KEY = "read-only-mode"; + +class DatabaseFileSourceThread { +public: + DatabaseFileSourceThread(std::shared_ptr<FileSource> onlineFileSource_, std::string cachePath) + : db(std::make_unique<OfflineDatabase>(cachePath)), onlineFileSource(onlineFileSource_) {} + + void request(const Resource& resource, ActorRef<FileSourceRequest> req) { + auto offlineResponse = db->get(resource); + if (!offlineResponse) { + offlineResponse.emplace(); + offlineResponse->noContent = true; + offlineResponse->error = + std::make_unique<Response::Error>(Response::Error::Reason::NotFound, "Not found in offline database"); + } else if (!offlineResponse->isUsable()) { + offlineResponse->error = + std::make_unique<Response::Error>(Response::Error::Reason::NotFound, "Cached resource is unusable"); + } + req.invoke(&FileSourceRequest::setResponse, *offlineResponse); + } + + void setDatabasePath(const std::string& path, std::function<void()> callback) { + db->changePath(path); + if (callback) { + callback(); + } + } + + void forward(const Resource& resource, const Response& response, std::function<void()> callback) { + db->put(resource, response); + if (callback) { + callback(); + } + } + + void resetDatabase(std::function<void(std::exception_ptr)> callback) { callback(db->resetDatabase()); } + + void packDatabase(std::function<void(std::exception_ptr)> callback) { callback(db->pack()); } + + void runPackDatabaseAutomatically(bool autopack) { db->runPackDatabaseAutomatically(autopack); } + + void put(const Resource& resource, const Response& response) { db->put(resource, response); } + + void invalidateAmbientCache(std::function<void(std::exception_ptr)> callback) { + callback(db->invalidateAmbientCache()); + } + + void clearAmbientCache(std::function<void(std::exception_ptr)> callback) { callback(db->clearAmbientCache()); } + + void setMaximumAmbientCacheSize(uint64_t size, std::function<void(std::exception_ptr)> callback) { + callback(db->setMaximumAmbientCacheSize(size)); + } + + void listRegions(std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) { + callback(db->listRegions()); + } + + void createRegion(const OfflineRegionDefinition& definition, + const OfflineRegionMetadata& metadata, + std::function<void(expected<OfflineRegion, std::exception_ptr>)> callback) { + callback(db->createRegion(definition, metadata)); + } + + void mergeOfflineRegions(const std::string& sideDatabasePath, + std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) { + callback(db->mergeDatabase(sideDatabasePath)); + } + + void updateMetadata(const int64_t regionID, + const OfflineRegionMetadata& metadata, + std::function<void(expected<OfflineRegionMetadata, std::exception_ptr>)> callback) { + callback(db->updateMetadata(regionID, metadata)); + } + + void getRegionStatus(int64_t regionID, + std::function<void(expected<OfflineRegionStatus, std::exception_ptr>)> callback) { + if (auto download = getDownload(regionID)) { + callback(download.value()->getStatus()); + } else { + callback(unexpected<std::exception_ptr>(download.error())); + } + } + + void deleteRegion(OfflineRegion region, std::function<void(std::exception_ptr)> callback) { + downloads.erase(region.getID()); + callback(db->deleteRegion(std::move(region))); + } + + void invalidateRegion(int64_t regionID, std::function<void(std::exception_ptr)> callback) { + callback(db->invalidateRegion(regionID)); + } + + void setRegionObserver(int64_t regionID, std::unique_ptr<OfflineRegionObserver> observer) { + if (auto download = getDownload(regionID)) { + download.value()->setObserver(std::move(observer)); + } + } + + void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) { + if (auto download = getDownload(regionID)) { + download.value()->setState(state); + } + } + + void setOfflineMapboxTileCountLimit(uint64_t limit) { db->setOfflineMapboxTileCountLimit(limit); } + + void reopenDatabaseReadOnlyForTesting() { db->reopenDatabaseReadOnlyForTesting(); } + +private: + expected<OfflineDownload*, std::exception_ptr> getDownload(int64_t regionID) { + if (!onlineFileSource) { + return unexpected<std::exception_ptr>( + std::make_exception_ptr(std::runtime_error("Network file source unavailable."))); + } + + auto it = downloads.find(regionID); + if (it != downloads.end()) { + return it->second.get(); + } + auto definition = db->getRegionDefinition(regionID); + if (!definition) { + return unexpected<std::exception_ptr>(definition.error()); + } + auto download = + std::make_unique<OfflineDownload>(regionID, std::move(definition.value()), *db, *onlineFileSource); + return downloads.emplace(regionID, std::move(download)).first->second.get(); + } + + std::unique_ptr<OfflineDatabase> db; + std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads; + std::shared_ptr<FileSource> onlineFileSource; +}; + +class DatabaseFileSource::Impl { +public: + Impl(std::shared_ptr<FileSource> onlineFileSource, const std::string& cachePath) + : thread(std::make_unique<util::Thread<DatabaseFileSourceThread>>( + "DatabaseFileSource", std::move(onlineFileSource), cachePath)) {} + + ActorRef<DatabaseFileSourceThread> actor() const { return thread->actor(); } + +private: + const std::unique_ptr<util::Thread<DatabaseFileSourceThread>> thread; +}; + +DatabaseFileSource::DatabaseFileSource(const ResourceOptions& options) + : impl(std::make_unique<Impl>(FileSourceManager::get()->getFileSource(FileSourceType::Network, options), + options.cachePath())) {} + +DatabaseFileSource::~DatabaseFileSource() = default; + +std::unique_ptr<AsyncRequest> DatabaseFileSource::request(const Resource& resource, Callback callback) { + auto req = std::make_unique<FileSourceRequest>(std::move(callback)); + impl->actor().invoke(&DatabaseFileSourceThread::request, resource, req->actor()); + return std::move(req); +} + +void DatabaseFileSource::forward(const Resource& res, const Response& response, std::function<void()> callback) { + std::function<void()> wrapper; + if (callback) { + wrapper = Scheduler::GetCurrent()->bindOnce(std::move(callback)); + } + impl->actor().invoke(&DatabaseFileSourceThread::forward, res, response, std::move(wrapper)); +} + +bool DatabaseFileSource::canRequest(const Resource& resource) const { + return resource.hasLoadingMethod(Resource::LoadingMethod::Cache) && + resource.url.rfind(mbgl::util::ASSET_PROTOCOL, 0) == std::string::npos && + resource.url.rfind(mbgl::util::FILE_PROTOCOL, 0) == std::string::npos; +} + +void DatabaseFileSource::setDatabasePath(const std::string& path, std::function<void()> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::setDatabasePath, path, std::move(callback)); +} + +void DatabaseFileSource::resetDatabase(std::function<void(std::exception_ptr)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::resetDatabase, std::move(callback)); +} + +void DatabaseFileSource::packDatabase(std::function<void(std::exception_ptr)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::packDatabase, std::move(callback)); +} + +void DatabaseFileSource::runPackDatabaseAutomatically(bool autopack) { + impl->actor().invoke(&DatabaseFileSourceThread::runPackDatabaseAutomatically, autopack); +} + +void DatabaseFileSource::put(const Resource& resource, const Response& response) { + impl->actor().invoke(&DatabaseFileSourceThread::put, resource, response); +} + +void DatabaseFileSource::invalidateAmbientCache(std::function<void(std::exception_ptr)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::invalidateAmbientCache, std::move(callback)); +} + +void DatabaseFileSource::clearAmbientCache(std::function<void(std::exception_ptr)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::clearAmbientCache, std::move(callback)); +} + +void DatabaseFileSource::setMaximumAmbientCacheSize(uint64_t size, std::function<void(std::exception_ptr)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::setMaximumAmbientCacheSize, size, std::move(callback)); +} + +void DatabaseFileSource::listOfflineRegions( + std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::listRegions, callback); +} + +void DatabaseFileSource::createOfflineRegion( + const OfflineRegionDefinition& definition, + const OfflineRegionMetadata& metadata, + std::function<void(expected<OfflineRegion, std::exception_ptr>)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::createRegion, definition, metadata, callback); +} + +void DatabaseFileSource::mergeOfflineRegions( + const std::string& sideDatabasePath, std::function<void(expected<OfflineRegions, std::exception_ptr>)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::mergeOfflineRegions, sideDatabasePath, callback); +} + +void DatabaseFileSource::updateOfflineMetadata( + const int64_t regionID, + const OfflineRegionMetadata& metadata, + std::function<void(expected<OfflineRegionMetadata, std::exception_ptr>)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::updateMetadata, regionID, metadata, callback); +} + +void DatabaseFileSource::deleteOfflineRegion(OfflineRegion region, std::function<void(std::exception_ptr)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::deleteRegion, std::move(region), callback); +} + +void DatabaseFileSource::invalidateOfflineRegion(OfflineRegion& region, + std::function<void(std::exception_ptr)> callback) { + impl->actor().invoke(&DatabaseFileSourceThread::invalidateRegion, region.getID(), callback); +} + +void DatabaseFileSource::setOfflineRegionObserver(OfflineRegion& region, + std::unique_ptr<OfflineRegionObserver> observer) { + impl->actor().invoke(&DatabaseFileSourceThread::setRegionObserver, region.getID(), std::move(observer)); +} + +void DatabaseFileSource::setOfflineRegionDownloadState(OfflineRegion& region, OfflineRegionDownloadState state) { + impl->actor().invoke(&DatabaseFileSourceThread::setRegionDownloadState, region.getID(), state); +} + +void DatabaseFileSource::getOfflineRegionStatus( + OfflineRegion& region, std::function<void(expected<OfflineRegionStatus, std::exception_ptr>)> callback) const { + impl->actor().invoke(&DatabaseFileSourceThread::getRegionStatus, region.getID(), callback); +} + +void DatabaseFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const { + impl->actor().invoke(&DatabaseFileSourceThread::setOfflineMapboxTileCountLimit, limit); +} + +void DatabaseFileSource::setProperty(const std::string& key, const mapbox::base::Value& value) { + if (key == READ_ONLY_MODE_KEY && value.getBool()) { + if (*value.getBool()) { + impl->actor().invoke(&DatabaseFileSourceThread::reopenDatabaseReadOnlyForTesting); + } + } else { + std::string message = "Resource provider does not support property " + key; + Log::Error(Event::General, message.c_str()); + } +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/storage/default_file_source.cpp b/platform/default/src/mbgl/storage/default_file_source.cpp deleted file mode 100644 index 2d96a5a9a2..0000000000 --- a/platform/default/src/mbgl/storage/default_file_source.cpp +++ /dev/null @@ -1,398 +0,0 @@ -#include <mbgl/storage/default_file_source.hpp> -#include <mbgl/storage/asset_file_source.hpp> -#include <mbgl/storage/file_source_request.hpp> -#include <mbgl/storage/local_file_source.hpp> -#include <mbgl/storage/online_file_source.hpp> -#include <mbgl/storage/offline_database.hpp> -#include <mbgl/storage/offline_download.hpp> -#include <mbgl/storage/resource_transform.hpp> - -#include <mbgl/util/platform.hpp> -#include <mbgl/util/url.hpp> -#include <mbgl/util/thread.hpp> -#include <mbgl/util/work_request.hpp> -#include <mbgl/util/stopwatch.hpp> - -#include <cassert> -#include <utility> - -namespace mbgl { - -class DefaultFileSource::Impl { -public: - Impl(std::shared_ptr<FileSource> assetFileSource_, std::string cachePath) - : assetFileSource(std::move(assetFileSource_)) - , localFileSource(std::make_unique<LocalFileSource>()) - , offlineDatabase(std::make_unique<OfflineDatabase>(std::move(cachePath))) { - } - - void setAPIBaseURL(const std::string& url) { - onlineFileSource.setAPIBaseURL(url); - } - - std::string getAPIBaseURL() const{ - return onlineFileSource.getAPIBaseURL(); - } - - void setAccessToken(const std::string& accessToken) { - onlineFileSource.setAccessToken(accessToken); - } - - std::string getAccessToken() const { - return onlineFileSource.getAccessToken(); - } - - void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) { - onlineFileSource.setResourceTransform(std::move(transform)); - } - - void setResourceCachePath(const std::string& path, optional<ActorRef<PathChangeCallback>>&& callback) { - offlineDatabase->changePath(path); - if (callback) { - callback->invoke(&PathChangeCallback::operator()); - } - } - - void listRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) { - callback(offlineDatabase->listRegions()); - } - - void createRegion(const OfflineRegionDefinition& definition, - const OfflineRegionMetadata& metadata, - std::function<void (expected<OfflineRegion, std::exception_ptr>)> callback) { - callback(offlineDatabase->createRegion(definition, metadata)); - } - - void mergeOfflineRegions(const std::string& sideDatabasePath, - std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) { - callback(offlineDatabase->mergeDatabase(sideDatabasePath)); - } - - void updateMetadata(const int64_t regionID, - const OfflineRegionMetadata& metadata, - std::function<void (expected<OfflineRegionMetadata, std::exception_ptr>)> callback) { - callback(offlineDatabase->updateMetadata(regionID, metadata)); - } - - void getRegionStatus(int64_t regionID, std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)> callback) { - if (auto download = getDownload(regionID)) { - callback(download.value()->getStatus()); - } else { - callback(unexpected<std::exception_ptr>(download.error())); - } - } - - void deleteRegion(OfflineRegion&& region, std::function<void(std::exception_ptr)> callback) { - downloads.erase(region.getID()); - callback(offlineDatabase->deleteRegion(std::move(region))); - } - - void invalidateRegion(int64_t regionID, std::function<void (std::exception_ptr)> callback) { - callback(offlineDatabase->invalidateRegion(regionID)); - } - - void setRegionObserver(int64_t regionID, std::unique_ptr<OfflineRegionObserver> observer) { - if (auto download = getDownload(regionID)) { - download.value()->setObserver(std::move(observer)); - } - } - - void setRegionDownloadState(int64_t regionID, OfflineRegionDownloadState state) { - if (auto download = getDownload(regionID)) { - download.value()->setState(state); - } - } - - void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) { - auto callback = [ref] (const Response& res) { - ref.invoke(&FileSourceRequest::setResponse, res); - }; - - if (AssetFileSource::acceptsURL(resource.url)) { - //Asset request - tasks[req] = assetFileSource->request(resource, callback); - } else if (LocalFileSource::acceptsURL(resource.url)) { - //Local file request - tasks[req] = localFileSource->request(resource, callback); - } else { - // Try the offline database - if (resource.hasLoadingMethod(Resource::LoadingMethod::Cache)) { - auto offlineResponse = offlineDatabase->get(resource); - - if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) { - if (!offlineResponse) { - // Ensure there's always a response that we can send, so the caller knows that - // there's no optional data available in the cache, when it's the only place - // we're supposed to load from. - offlineResponse.emplace(); - offlineResponse->noContent = true; - offlineResponse->error = std::make_unique<Response::Error>( - Response::Error::Reason::NotFound, "Not found in offline database"); - } else if (!offlineResponse->isUsable()) { - // Don't return resources the server requested not to show when they're stale. - // Even if we can't directly use the response, we may still use it to send a - // conditional HTTP request, which is why we're saving it above. - offlineResponse->error = std::make_unique<Response::Error>( - Response::Error::Reason::NotFound, "Cached resource is unusable"); - } - callback(*offlineResponse); - } else if (offlineResponse) { - // Copy over the fields so that we can use them when making a refresh request. - resource.priorModified = offlineResponse->modified; - resource.priorExpires = offlineResponse->expires; - resource.priorEtag = offlineResponse->etag; - resource.priorData = offlineResponse->data; - - if (offlineResponse->isUsable()) { - callback(*offlineResponse); - // Set the priority of existing resource to low if it's expired but usable. - resource.setPriority(Resource::Priority::Low); - } - } - } - - // Get from the online file source - if (resource.hasLoadingMethod(Resource::LoadingMethod::Network)) { - MBGL_TIMING_START(watch); - tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) { - this->offlineDatabase->put(resource, onlineResponse); - if (resource.kind == Resource::Kind::Tile) { - // onlineResponse.data will be null if data not modified - MBGL_TIMING_FINISH(watch, - " Action: " << "Requesting," << - " URL: " << resource.url.c_str() << - " Size: " << (onlineResponse.data != nullptr ? onlineResponse.data->size() : 0) << "B," << - " Time") - } - callback(onlineResponse); - }); - } - } - } - - void cancel(AsyncRequest* req) { - tasks.erase(req); - } - - void setOfflineMapboxTileCountLimit(uint64_t limit) { - offlineDatabase->setOfflineMapboxTileCountLimit(limit); - } - - void setOnlineStatus(const bool status) { - onlineFileSource.setOnlineStatus(status); - } - - void reopenDatabaseReadOnlyForTesting() { offlineDatabase->reopenDatabaseReadOnlyForTesting(); } - - void setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) { - onlineFileSource.setMaximumConcurrentRequests(maximumConcurrentRequests_); - } - - void put(const Resource& resource, const Response& response) { - offlineDatabase->put(resource, response); - } - - void resetDatabase(std::function<void (std::exception_ptr)> callback) { - callback(offlineDatabase->resetDatabase()); - } - - void invalidateAmbientCache(std::function<void (std::exception_ptr)> callback) { - callback(offlineDatabase->invalidateAmbientCache()); - } - - void clearAmbientCache(std::function<void (std::exception_ptr)> callback) { - callback(offlineDatabase->clearAmbientCache()); - } - - void setMaximumAmbientCacheSize(uint64_t size, std::function<void (std::exception_ptr)> callback) { - callback(offlineDatabase->setMaximumAmbientCacheSize(size)); - } - - void packDatabase(std::function<void(std::exception_ptr)> callback) { callback(offlineDatabase->pack()); } - - void runPackDatabaseAutomatically(bool autopack) { offlineDatabase->runPackDatabaseAutomatically(autopack); } - -private: - expected<OfflineDownload*, std::exception_ptr> getDownload(int64_t regionID) { - auto it = downloads.find(regionID); - if (it != downloads.end()) { - return it->second.get(); - } - auto definition = offlineDatabase->getRegionDefinition(regionID); - if (!definition) { - return unexpected<std::exception_ptr>(definition.error()); - } - auto download = std::make_unique<OfflineDownload>(regionID, std::move(definition.value()), - *offlineDatabase, onlineFileSource); - return downloads.emplace(regionID, std::move(download)).first->second.get(); - } - - // shared so that destruction is done on the creating thread - const std::shared_ptr<FileSource> assetFileSource; - const std::unique_ptr<FileSource> localFileSource; - std::unique_ptr<OfflineDatabase> offlineDatabase; - OnlineFileSource onlineFileSource; - std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks; - std::unordered_map<int64_t, std::unique_ptr<OfflineDownload>> downloads; -}; - -DefaultFileSource::DefaultFileSource(const std::string& cachePath, const std::string& assetPath, bool supportCacheOnlyRequests_) - : DefaultFileSource(cachePath, std::make_unique<AssetFileSource>(assetPath), supportCacheOnlyRequests_) { -} - -DefaultFileSource::DefaultFileSource(const std::string& cachePath, std::unique_ptr<FileSource>&& assetFileSource_, bool supportCacheOnlyRequests_) - : assetFileSource(std::move(assetFileSource_)) - , impl(std::make_unique<util::Thread<Impl>>("DefaultFileSource", assetFileSource, cachePath)) - , supportCacheOnlyRequests(supportCacheOnlyRequests_) { -} - -DefaultFileSource::~DefaultFileSource() = default; - -bool DefaultFileSource::supportsCacheOnlyRequests() const { - return supportCacheOnlyRequests; -} - -void DefaultFileSource::setAPIBaseURL(const std::string& baseURL) { - impl->actor().invoke(&Impl::setAPIBaseURL, baseURL); - - { - std::lock_guard<std::mutex> lock(cachedBaseURLMutex); - cachedBaseURL = baseURL; - } -} - -std::string DefaultFileSource::getAPIBaseURL() { - std::lock_guard<std::mutex> lock(cachedBaseURLMutex); - return cachedBaseURL; -} - -void DefaultFileSource::setAccessToken(const std::string& accessToken) { - impl->actor().invoke(&Impl::setAccessToken, accessToken); - - { - std::lock_guard<std::mutex> lock(cachedAccessTokenMutex); - cachedAccessToken = accessToken; - } -} - -std::string DefaultFileSource::getAccessToken() { - std::lock_guard<std::mutex> lock(cachedAccessTokenMutex); - return cachedAccessToken; -} - -void DefaultFileSource::setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) { - impl->actor().invoke(&Impl::setResourceTransform, std::move(transform)); -} - -void DefaultFileSource::setResourceCachePath(const std::string& path, optional<ActorRef<PathChangeCallback>>&& callback) { - impl->actor().invoke(&Impl::setResourceCachePath, path, std::move(callback)); -} - -std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resource, Callback callback) { - auto req = std::make_unique<FileSourceRequest>(std::move(callback)); - - req->onCancel([fs = impl->actor(), req = req.get()] () { fs.invoke(&Impl::cancel, req); }); - - impl->actor().invoke(&Impl::request, req.get(), resource, req->actor()); - - return std::move(req); -} - -void DefaultFileSource::listOfflineRegions(std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) { - impl->actor().invoke(&Impl::listRegions, callback); -} - -void DefaultFileSource::createOfflineRegion(const OfflineRegionDefinition& definition, - const OfflineRegionMetadata& metadata, - std::function<void (expected<OfflineRegion, std::exception_ptr>)> callback) { - impl->actor().invoke(&Impl::createRegion, definition, metadata, callback); -} - -void DefaultFileSource::mergeOfflineRegions(const std::string& sideDatabasePath, - std::function<void (expected<OfflineRegions, std::exception_ptr>)> callback) { - impl->actor().invoke(&Impl::mergeOfflineRegions, sideDatabasePath, callback); -} - -void DefaultFileSource::updateOfflineMetadata(const int64_t regionID, - const OfflineRegionMetadata& metadata, - std::function<void (expected<OfflineRegionMetadata, - std::exception_ptr>)> callback) { - impl->actor().invoke(&Impl::updateMetadata, regionID, metadata, callback); -} - -void DefaultFileSource::deleteOfflineRegion(OfflineRegion&& region, std::function<void(std::exception_ptr)> callback) { - impl->actor().invoke(&Impl::deleteRegion, std::move(region), callback); -} - -void DefaultFileSource::invalidateOfflineRegion(OfflineRegion& region, - std::function<void(std::exception_ptr)> callback) { - impl->actor().invoke(&Impl::invalidateRegion, region.getID(), callback); -} - -void DefaultFileSource::setOfflineRegionObserver(OfflineRegion& region, std::unique_ptr<OfflineRegionObserver> observer) { - impl->actor().invoke(&Impl::setRegionObserver, region.getID(), std::move(observer)); -} - -void DefaultFileSource::setOfflineRegionDownloadState(OfflineRegion& region, OfflineRegionDownloadState state) { - impl->actor().invoke(&Impl::setRegionDownloadState, region.getID(), state); -} - -void DefaultFileSource::getOfflineRegionStatus(OfflineRegion& region, std::function<void (expected<OfflineRegionStatus, std::exception_ptr>)> callback) const { - impl->actor().invoke(&Impl::getRegionStatus, region.getID(), callback); -} - -void DefaultFileSource::setOfflineMapboxTileCountLimit(uint64_t limit) const { - impl->actor().invoke(&Impl::setOfflineMapboxTileCountLimit, limit); -} - -void DefaultFileSource::pause() { - impl->pause(); -} - -void DefaultFileSource::resume() { - impl->resume(); -} - -void DefaultFileSource::put(const Resource& resource, const Response& response) { - impl->actor().invoke(&Impl::put, resource, response); -} - -void DefaultFileSource::resetDatabase(std::function<void (std::exception_ptr)> callback) { - impl->actor().invoke(&Impl::resetDatabase, std::move(callback)); -} - -void DefaultFileSource::packDatabase(std::function<void(std::exception_ptr)> callback) { - impl->actor().invoke(&Impl::packDatabase, std::move(callback)); -} - -void DefaultFileSource::runPackDatabaseAutomatically(bool autopack) { - impl->actor().invoke(&Impl::runPackDatabaseAutomatically, autopack); -} - -void DefaultFileSource::invalidateAmbientCache(std::function<void (std::exception_ptr)> callback) { - impl->actor().invoke(&Impl::invalidateAmbientCache, std::move(callback)); -} - -void DefaultFileSource::clearAmbientCache(std::function<void(std::exception_ptr)> callback) { - impl->actor().invoke(&Impl::clearAmbientCache, std::move(callback)); -} - -void DefaultFileSource::setMaximumAmbientCacheSize(uint64_t size, std::function<void (std::exception_ptr)> callback) { - impl->actor().invoke(&Impl::setMaximumAmbientCacheSize, size, std::move(callback)); -} - -// For testing only: - -void DefaultFileSource::setOnlineStatus(const bool status) { - impl->actor().invoke(&Impl::setOnlineStatus, status); -} - -void DefaultFileSource::reopenDatabaseReadOnlyForTesting() { - impl->actor().invoke(&Impl::reopenDatabaseReadOnlyForTesting); -} - -void DefaultFileSource::setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) { - impl->actor().invoke(&Impl::setMaximumConcurrentRequests, maximumConcurrentRequests_); -} - -} // namespace mbgl diff --git a/platform/default/src/mbgl/storage/file_source.cpp b/platform/default/src/mbgl/storage/file_source.cpp deleted file mode 100644 index 4e800cc8f4..0000000000 --- a/platform/default/src/mbgl/storage/file_source.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include <mbgl/storage/resource_options.hpp> -#include <mbgl/storage/default_file_source.hpp> - -#include <memory> - -namespace mbgl { - -std::shared_ptr<FileSource> FileSource::createPlatformFileSource(const ResourceOptions& options) { - auto fileSource = std::make_shared<DefaultFileSource>(options.cachePath(), options.assetPath(), options.supportsCacheOnlyRequests()); - fileSource->setAccessToken(options.accessToken()); - fileSource->setAPIBaseURL(options.baseURL()); - return fileSource; -} - -} // namespace mbgl diff --git a/platform/default/src/mbgl/storage/file_source_manager.cpp b/platform/default/src/mbgl/storage/file_source_manager.cpp new file mode 100644 index 0000000000..2981096dac --- /dev/null +++ b/platform/default/src/mbgl/storage/file_source_manager.cpp @@ -0,0 +1,43 @@ +#include <mbgl/storage/asset_file_source.hpp> +#include <mbgl/storage/database_file_source.hpp> +#include <mbgl/storage/file_source_manager.hpp> +#include <mbgl/storage/local_file_source.hpp> +#include <mbgl/storage/main_resource_loader.hpp> +#include <mbgl/storage/online_file_source.hpp> +#include <mbgl/storage/resource_options.hpp> + +namespace mbgl { + +class DefaultFileSourceManagerImpl final : public FileSourceManager { +public: + DefaultFileSourceManagerImpl() { + registerFileSourceFactory(FileSourceType::ResourceLoader, [](const ResourceOptions& options) { + return std::make_unique<MainResourceLoader>(options); + }); + + registerFileSourceFactory(FileSourceType::Asset, [](const ResourceOptions& options) { + return std::make_unique<AssetFileSource>(options.assetPath()); + }); + + registerFileSourceFactory(FileSourceType::Database, [](const ResourceOptions& options) { + return std::make_unique<DatabaseFileSource>(options); + }); + + registerFileSourceFactory(FileSourceType::FileSystem, + [](const ResourceOptions&) { return std::make_unique<LocalFileSource>(); }); + + registerFileSourceFactory(FileSourceType::Network, [](const ResourceOptions& options) { + auto networkSource = std::make_unique<OnlineFileSource>(); + networkSource->setProperty(ACCESS_TOKEN_KEY, options.accessToken()); + networkSource->setProperty(API_BASE_URL_KEY, options.baseURL()); + return networkSource; + }); + } +}; + +FileSourceManager* FileSourceManager::get() noexcept { + static DefaultFileSourceManagerImpl instance; + return &instance; +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/storage/local_file_source.cpp b/platform/default/src/mbgl/storage/local_file_source.cpp index ca2eedc7ba..54f12baf79 100644 --- a/platform/default/src/mbgl/storage/local_file_source.cpp +++ b/platform/default/src/mbgl/storage/local_file_source.cpp @@ -1,15 +1,17 @@ -#include <mbgl/storage/local_file_source.hpp> #include <mbgl/storage/file_source_request.hpp> #include <mbgl/storage/local_file_request.hpp> +#include <mbgl/storage/local_file_source.hpp> +#include <mbgl/storage/resource.hpp> #include <mbgl/storage/response.hpp> +#include <mbgl/util/constants.hpp> #include <mbgl/util/string.hpp> #include <mbgl/util/thread.hpp> #include <mbgl/util/url.hpp> namespace { - -const std::string fileProtocol = "file://"; - +bool acceptsURL(const std::string& url) { + return 0 == url.rfind(mbgl::util::FILE_PROTOCOL, 0); +} } // namespace namespace mbgl { @@ -28,7 +30,7 @@ public: } // Cut off the protocol and prefix with path. - const auto path = mbgl::util::percentDecode(url.substr(fileProtocol.size())); + const auto path = mbgl::util::percentDecode(url.substr(std::char_traits<char>::length(util::FILE_PROTOCOL))); requestLocalFile(path, std::move(req)); } }; @@ -47,8 +49,16 @@ std::unique_ptr<AsyncRequest> LocalFileSource::request(const Resource& resource, return std::move(req); } -bool LocalFileSource::acceptsURL(const std::string& url) { - return 0 == url.rfind(fileProtocol, 0); +bool LocalFileSource::canRequest(const Resource& resource) const { + return acceptsURL(resource.url); +} + +void LocalFileSource::pause() { + impl->pause(); +} + +void LocalFileSource::resume() { + impl->resume(); } } // namespace mbgl diff --git a/platform/default/src/mbgl/storage/main_resource_loader.cpp b/platform/default/src/mbgl/storage/main_resource_loader.cpp new file mode 100644 index 0000000000..e39ca8ef47 --- /dev/null +++ b/platform/default/src/mbgl/storage/main_resource_loader.cpp @@ -0,0 +1,223 @@ +#include <mbgl/actor/actor.hpp> +#include <mbgl/actor/scheduler.hpp> +#include <mbgl/storage/file_source_manager.hpp> +#include <mbgl/storage/file_source_request.hpp> +#include <mbgl/storage/main_resource_loader.hpp> +#include <mbgl/storage/resource.hpp> +#include <mbgl/storage/resource_options.hpp> +#include <mbgl/util/stopwatch.hpp> + +#include <cassert> + +namespace mbgl { + +class ResourceLoaderRequestor { +public: + explicit ResourceLoaderRequestor(MainResourceLoader::Impl& impl_); + void request(AsyncRequest*, Resource, ActorRef<FileSourceRequest>); + void cancel(AsyncRequest*); + void pause(); + void resume(); + +private: + MainResourceLoader::Impl& impl; +}; + +class MainResourceLoader::Impl { +public: + Impl(std::shared_ptr<FileSource> assetFileSource_, + std::shared_ptr<FileSource> databaseFileSource_, + std::shared_ptr<FileSource> localFileSource_, + std::shared_ptr<FileSource> onlineFileSource_) + : assetFileSource(std::move(assetFileSource_)), + databaseFileSource(std::move(databaseFileSource_)), + localFileSource(std::move(localFileSource_)), + onlineFileSource(std::move(onlineFileSource_)), + supportsCacheOnlyRequests_(bool(databaseFileSource)), + requestor(std::make_unique<Actor<ResourceLoaderRequestor>>(*Scheduler::GetCurrent(), *this)) {} + + std::unique_ptr<AsyncRequest> request(const Resource& resource, Callback callback) { + auto req = std::make_unique<FileSourceRequest>(std::move(callback)); + req->onCancel([actorRef = requestor->self(), req = req.get()]() { + actorRef.invoke(&ResourceLoaderRequestor::cancel, req); + }); + requestor->self().invoke(&ResourceLoaderRequestor::request, req.get(), resource, req->actor()); + return std::move(req); + } + + bool canRequest(const Resource& resource) const { + return (assetFileSource && assetFileSource->canRequest(resource)) || + (localFileSource && localFileSource->canRequest(resource)) || + (databaseFileSource && databaseFileSource->canRequest(resource)) || + (onlineFileSource && onlineFileSource->canRequest(resource)); + } + + bool supportsCacheOnlyRequests() const { return supportsCacheOnlyRequests_; } + + void pause() { requestor->self().invoke(&ResourceLoaderRequestor::pause); } + + void resume() { requestor->self().invoke(&ResourceLoaderRequestor::resume); } + +private: + void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) { + auto callback = [ref](const Response& res) { ref.invoke(&FileSourceRequest::setResponse, res); }; + + auto requestFromNetwork = [=](const Resource& res, + std::unique_ptr<AsyncRequest> parent) -> std::unique_ptr<AsyncRequest> { + if (!onlineFileSource || !onlineFileSource->canRequest(resource)) { + return parent; + } + + // Keep parent request alive while chained request is being processed. + std::shared_ptr<AsyncRequest> parentKeepAlive = std::move(parent); + + MBGL_TIMING_START(watch); + return onlineFileSource->request(res, [=, ptr = parentKeepAlive](Response response) { + if (databaseFileSource) { + databaseFileSource->forward(res, response); + } + if (res.kind == Resource::Kind::Tile) { + // onlineResponse.data will be null if data not modified + MBGL_TIMING_FINISH(watch, + " Action: " + << "Requesting," + << " URL: " << res.url.c_str() << " Size: " + << (response.data != nullptr ? response.data->size() : 0) << "B," + << " Time") + } + callback(response); + }); + }; + + // Initial tasksSize is used to check whether any of + // the sources were able to request a resource. + const std::size_t tasksSize = tasks.size(); + + // Waterfall resource request processing and return early once resource was requested. + if (assetFileSource && assetFileSource->canRequest(resource)) { + // Asset request + tasks[req] = assetFileSource->request(resource, callback); + } else if (localFileSource && localFileSource->canRequest(resource)) { + // Local file request + tasks[req] = localFileSource->request(resource, callback); + } else if (databaseFileSource && databaseFileSource->canRequest(resource)) { + // Try cache only request if needed. + if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) { + tasks[req] = databaseFileSource->request(resource, callback); + } else { + // Cache request with fallback to network with cache control + tasks[req] = databaseFileSource->request(resource, [=](Response response) { + Resource res = resource; + + // Resource is in the cache + if (!response.noContent) { + if (response.isUsable()) { + callback(response); + // Set the priority of existing resource to low if it's expired but usable. + res.setPriority(Resource::Priority::Low); + } + + // Copy response fields for cache control request + res.priorModified = response.modified; + res.priorExpires = response.expires; + res.priorEtag = response.etag; + res.priorData = response.data; + } + + tasks[req] = requestFromNetwork(res, std::move(tasks[req])); + }); + } + } else if (auto networkReq = requestFromNetwork(resource, nullptr)) { + // Get from the online file source + tasks[req] = std::move(networkReq); + } + + // If no new tasks were added, notify client that request cannot be processed. + if (tasks.size() == tasksSize) { + Response response; + response.noContent = true; + response.error = + std::make_unique<Response::Error>(Response::Error::Reason::Other, "Unsupported resource request."); + callback(response); + } + } + + void pauseInternal() { + if (assetFileSource) assetFileSource->pause(); + if (databaseFileSource) databaseFileSource->pause(); + if (localFileSource) localFileSource->pause(); + if (onlineFileSource) onlineFileSource->pause(); + } + + void resumeInternal() { + if (assetFileSource) assetFileSource->resume(); + if (databaseFileSource) databaseFileSource->resume(); + if (localFileSource) localFileSource->resume(); + if (onlineFileSource) onlineFileSource->resume(); + } + + void cancel(AsyncRequest* req) { + assert(req); + tasks.erase(req); + } + +private: + friend class ResourceLoaderRequestor; + const std::shared_ptr<FileSource> assetFileSource; + const std::shared_ptr<FileSource> databaseFileSource; + const std::shared_ptr<FileSource> localFileSource; + const std::shared_ptr<FileSource> onlineFileSource; + const bool supportsCacheOnlyRequests_; + std::unique_ptr<Actor<ResourceLoaderRequestor>> requestor; + std::unordered_map<AsyncRequest*, std::unique_ptr<AsyncRequest>> tasks; +}; + +ResourceLoaderRequestor::ResourceLoaderRequestor(MainResourceLoader::Impl& impl_) : impl(impl_) {} + +void ResourceLoaderRequestor::request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) { + assert(req); + impl.request(req, std::move(resource), std::move(ref)); +} + +void ResourceLoaderRequestor::cancel(AsyncRequest* req) { + assert(req); + impl.cancel(req); +} + +void ResourceLoaderRequestor::pause() { + impl.pauseInternal(); +} + +void ResourceLoaderRequestor::resume() { + impl.resumeInternal(); +} + +MainResourceLoader::MainResourceLoader(const ResourceOptions& options) + : impl(std::make_unique<Impl>(FileSourceManager::get()->getFileSource(FileSourceType::Asset, options), + FileSourceManager::get()->getFileSource(FileSourceType::Database, options), + FileSourceManager::get()->getFileSource(FileSourceType::FileSystem, options), + FileSourceManager::get()->getFileSource(FileSourceType::Network, options))) {} + +MainResourceLoader::~MainResourceLoader() = default; + +bool MainResourceLoader::supportsCacheOnlyRequests() const { + return impl->supportsCacheOnlyRequests(); +} + +std::unique_ptr<AsyncRequest> MainResourceLoader::request(const Resource& resource, Callback callback) { + return impl->request(resource, std::move(callback)); +} + +bool MainResourceLoader::canRequest(const Resource& resource) const { + return impl->canRequest(resource); +} + +void MainResourceLoader::pause() { + impl->pause(); +} + +void MainResourceLoader::resume() { + impl->resume(); +} + +} // namespace mbgl diff --git a/platform/default/src/mbgl/storage/offline_download.cpp b/platform/default/src/mbgl/storage/offline_download.cpp index 98eb1d3884..32fcb4f625 100644 --- a/platform/default/src/mbgl/storage/offline_download.cpp +++ b/platform/default/src/mbgl/storage/offline_download.cpp @@ -1,4 +1,3 @@ -#include <mbgl/storage/online_file_source.hpp> #include <mbgl/storage/offline_database.hpp> #include <mbgl/storage/offline_download.hpp> #include <mbgl/storage/resource.hpp> @@ -90,11 +89,11 @@ uint64_t tileCount(const OfflineRegionDefinition& definition, style::SourceType // OfflineDownload OfflineDownload::OfflineDownload(int64_t id_, - OfflineRegionDefinition&& definition_, + OfflineRegionDefinition definition_, OfflineDatabase& offlineDatabase_, - OnlineFileSource& onlineFileSource_) + FileSource& onlineFileSource_) : id(id_), - definition(definition_), + definition(std::move(definition_)), offlineDatabase(offlineDatabase_), onlineFileSource(onlineFileSource_) { setObserver(nullptr); @@ -369,7 +368,13 @@ void OfflineDownload::continueDownload() { if (resourcesToBeMarkedAsUsed.size() >= kMarkBatchSize) markPendingUsedResources(); - while (!resourcesRemaining.empty() && requests.size() < onlineFileSource.getMaximumConcurrentRequests()) { + uint32_t maxConcurrentRequests = util::DEFAULT_MAXIMUM_CONCURRENT_REQUESTS; + auto value = onlineFileSource.getProperty("max-concurrent-requests"); + if (uint64_t* maxRequests = value.getUint()) { + maxConcurrentRequests = static_cast<uint32_t>(*maxRequests); + } + + while (!resourcesRemaining.empty() && requests.size() < maxConcurrentRequests) { ensureResource(std::move(resourcesRemaining.front())); resourcesRemaining.pop_front(); } diff --git a/platform/default/src/mbgl/storage/online_file_source.cpp b/platform/default/src/mbgl/storage/online_file_source.cpp index f4225bdf3f..0f5b438e1d 100644 --- a/platform/default/src/mbgl/storage/online_file_source.cpp +++ b/platform/default/src/mbgl/storage/online_file_source.cpp @@ -2,52 +2,59 @@ #include <mbgl/storage/http_file_source.hpp> #include <mbgl/storage/network_status.hpp> +#include <mbgl/storage/file_source_request.hpp> #include <mbgl/storage/resource_transform.hpp> #include <mbgl/storage/response.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/actor/mailbox.hpp> +#include <mbgl/util/async_task.hpp> +#include <mbgl/util/chrono.hpp> #include <mbgl/util/constants.hpp> -#include <mbgl/util/mapbox.hpp> #include <mbgl/util/exception.hpp> -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/async_task.hpp> +#include <mbgl/util/http_timeout.hpp> +#include <mbgl/util/mapbox.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/util/thread.hpp> #include <mbgl/util/timer.hpp> -#include <mbgl/util/http_timeout.hpp> #include <algorithm> #include <cassert> #include <list> -#include <unordered_set> +#include <map> #include <unordered_map> +#include <unordered_set> namespace mbgl { -static uint32_t DEFAULT_MAXIMUM_CONCURRENT_REQUESTS = 20; +// For testing only +constexpr const char* ONLINE_STATUS_KEY = "online-status"; -class OnlineFileRequest : public AsyncRequest { -public: - using Callback = std::function<void (Response)>; +class OnlineFileSourceThread; - OnlineFileRequest(Resource, Callback, OnlineFileSource::Impl&); - ~OnlineFileRequest() override; +struct OnlineFileRequest { + using Callback = std::function<void(Response)>; + + OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSourceThread& impl_); + ~OnlineFileRequest(); void networkIsReachableAgain(); void schedule(); void schedule(optional<Timestamp> expires); void completed(Response); - void setTransformedURL(const std::string&& url); + void setTransformedURL(const std::string& url); ActorRef<OnlineFileRequest> actor(); + void onCancel(std::function<void()>); - OnlineFileSource::Impl& impl; + OnlineFileSourceThread& impl; Resource resource; std::unique_ptr<AsyncRequest> request; util::Timer timer; Callback callback; + std::function<void()> cancelCallback = nullptr; std::shared_ptr<Mailbox> mailbox; // Counts the number of times a response was already expired when received. We're using @@ -62,101 +69,99 @@ public: optional<Timestamp> retryAfter; }; -class OnlineFileSource::Impl { +class OnlineFileSourceThread { public: - Impl() { + OnlineFileSourceThread() { NetworkStatus::Subscribe(&reachability); - setMaximumConcurrentRequests(DEFAULT_MAXIMUM_CONCURRENT_REQUESTS); + setMaximumConcurrentRequests(util::DEFAULT_MAXIMUM_CONCURRENT_REQUESTS); + } + + ~OnlineFileSourceThread() { NetworkStatus::Unsubscribe(&reachability); } + + void request(AsyncRequest* req, Resource resource, ActorRef<FileSourceRequest> ref) { + auto callback = [ref](const Response& res) { ref.invoke(&FileSourceRequest::setResponse, res); }; + tasks[req] = std::make_unique<OnlineFileRequest>(std::move(resource), std::move(callback), *this); } - ~Impl() { - NetworkStatus::Unsubscribe(&reachability); + void cancel(AsyncRequest* req) { + auto it = tasks.find(req); + assert(it != tasks.end()); + remove(it->second.get()); + tasks.erase(it); } - void add(OnlineFileRequest* request) { - allRequests.insert(request); + void add(OnlineFileRequest* req) { + allRequests.insert(req); if (resourceTransform) { // Request the ResourceTransform actor a new url and replace the resource url with the // transformed one before proceeding to schedule the request. - resourceTransform->invoke(&ResourceTransform::transform, - request->resource.kind, - std::move(request->resource.url), - [ref = request->actor()](const std::string&& url) { - ref.invoke(&OnlineFileRequest::setTransformedURL, url); - }); + resourceTransform.transform( + req->resource.kind, req->resource.url, [ref = req->actor()](const std::string& url) { + ref.invoke(&OnlineFileRequest::setTransformedURL, url); + }); } else { - request->schedule(); + req->schedule(); } } - void remove(OnlineFileRequest* request) { - allRequests.erase(request); - if (activeRequests.erase(request)) { + void remove(OnlineFileRequest* req) { + allRequests.erase(req); + if (activeRequests.erase(req)) { activatePendingRequest(); } else { - pendingRequests.remove(request); + pendingRequests.remove(req); } } - void activateOrQueueRequest(OnlineFileRequest* request) { - assert(allRequests.find(request) != allRequests.end()); - assert(activeRequests.find(request) == activeRequests.end()); - assert(!request->request); + void activateOrQueueRequest(OnlineFileRequest* req) { + assert(allRequests.find(req) != allRequests.end()); + assert(activeRequests.find(req) == activeRequests.end()); + assert(!req->request); if (activeRequests.size() >= getMaximumConcurrentRequests()) { - queueRequest(request); + queueRequest(req); } else { - activateRequest(request); + activateRequest(req); } } - void queueRequest(OnlineFileRequest* request) { - pendingRequests.insert(request); - } + void queueRequest(OnlineFileRequest* req) { pendingRequests.insert(req); } - void activateRequest(OnlineFileRequest* request) { + void activateRequest(OnlineFileRequest* req) { auto callback = [=](Response response) { - activeRequests.erase(request); - request->request.reset(); - request->completed(response); + activeRequests.erase(req); + req->request.reset(); + req->completed(response); activatePendingRequest(); }; - activeRequests.insert(request); + activeRequests.insert(req); if (online) { - request->request = httpFileSource.request(request->resource, callback); + req->request = httpFileSource.request(req->resource, callback); } else { Response response; response.error = std::make_unique<Response::Error>(Response::Error::Reason::Connection, "Online connectivity is disabled."); callback(response); } - } void activatePendingRequest() { + auto req = pendingRequests.pop(); - auto request = pendingRequests.pop(); - - if (request) { - activateRequest(*request); + if (req) { + activateRequest(*req); } } - bool isPending(OnlineFileRequest* request) { - return pendingRequests.contains(request); - } + bool isPending(OnlineFileRequest* req) { return pendingRequests.contains(req); } - bool isActive(OnlineFileRequest* request) { - return activeRequests.find(request) != activeRequests.end(); - } + bool isActive(OnlineFileRequest* req) { return activeRequests.find(req) != activeRequests.end(); } - void setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) { - resourceTransform = std::move(transform); - } + void setResourceTransform(ResourceTransform transform) { resourceTransform = std::move(transform); } - void setOnlineStatus(const bool status) { + void setOnlineStatus(bool status) { online = status; if (online) { networkIsReachableAgain(); @@ -171,20 +176,27 @@ public: maximumConcurrentRequests = maximumConcurrentRequests_; } + void setAPIBaseURL(const std::string& t) { apiBaseURL = t; } + std::string getAPIBaseURL() const { return apiBaseURL; } + + void setAccessToken(const std::string& t) { accessToken = t; } + std::string getAccessToken() const { return accessToken; } + private: + friend struct OnlineFileRequest; void networkIsReachableAgain() { // Notify regular priority requests. - for (auto& request : allRequests) { - if (request->resource.priority == Resource::Priority::Regular) { - request->networkIsReachableAgain(); + for (auto& req : allRequests) { + if (req->resource.priority == Resource::Priority::Regular) { + req->networkIsReachableAgain(); } } // Notify low priority requests. - for (auto& request : allRequests) { - if (request->resource.priority == Resource::Priority::Low) { - request->networkIsReachableAgain(); + for (auto& req : allRequests) { + if (req->resource.priority == Resource::Priority::Low) { + req->networkIsReachableAgain(); } } } @@ -231,7 +243,6 @@ private: } } - optional<OnlineFileRequest*> pop() { if (queue.empty()) { return optional<OnlineFileRequest*>(); @@ -252,7 +263,7 @@ private: }; - optional<ActorRef<ResourceTransform>> resourceTransform; + ResourceTransform resourceTransform; /** * The lifetime of a request is: @@ -274,56 +285,99 @@ private: bool online = true; uint32_t maximumConcurrentRequests; HTTPFileSource httpFileSource; - util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) }; + util::AsyncTask reachability{std::bind(&OnlineFileSourceThread::networkIsReachableAgain, this)}; + std::string accessToken; + std::string apiBaseURL = mbgl::util::API_BASE_URL; + std::map<AsyncRequest*, std::unique_ptr<OnlineFileRequest>> tasks; }; -OnlineFileSource::OnlineFileSource() - : impl(std::make_unique<Impl>()) { -} +class OnlineFileSource::Impl { +public: + Impl() : thread(std::make_unique<util::Thread<OnlineFileSourceThread>>("OnlineFileSource")) {} -OnlineFileSource::~OnlineFileSource() = default; + std::unique_ptr<AsyncRequest> request(Callback callback, Resource res) { + auto req = std::make_unique<FileSourceRequest>(std::move(callback)); + req->onCancel( + [actorRef = thread->actor(), req = req.get()]() { actorRef.invoke(&OnlineFileSourceThread::cancel, req); }); + thread->actor().invoke(&OnlineFileSourceThread::request, req.get(), std::move(res), req->actor()); + return std::move(req); + } -std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource, Callback callback) { - Resource res = resource; + void pause() { thread->pause(); } - switch (resource.kind) { - case Resource::Kind::Unknown: - case Resource::Kind::Image: - break; + void resume() { thread->resume(); } + + void setResourceTransform(ResourceTransform transform) { + thread->actor().invoke(&OnlineFileSourceThread::setResourceTransform, std::move(transform)); + } + + void setOnlineStatus(bool status) { thread->actor().invoke(&OnlineFileSourceThread::setOnlineStatus, status); } - case Resource::Kind::Style: - res.url = mbgl::util::mapbox::normalizeStyleURL(apiBaseURL, resource.url, accessToken); - break; + void setAPIBaseURL(const mapbox::base::Value& value) { + if (auto* baseURL = value.getString()) { + thread->actor().invoke(&OnlineFileSourceThread::setAPIBaseURL, *baseURL); + { + std::lock_guard<std::mutex> lock(cachedBaseURLMutex); + cachedBaseURL = *baseURL; + } + } else { + Log::Error(Event::General, "Invalid api-base-url property value type."); + } + } - case Resource::Kind::Source: - res.url = util::mapbox::normalizeSourceURL(apiBaseURL, resource.url, accessToken); - break; + std::string getAPIBaseURL() const { + std::lock_guard<std::mutex> lock(cachedBaseURLMutex); + return cachedBaseURL; + } - case Resource::Kind::Glyphs: - res.url = util::mapbox::normalizeGlyphsURL(apiBaseURL, resource.url, accessToken); - break; + void setMaximumConcurrentRequests(const mapbox::base::Value& value) { + if (auto* maximumConcurrentRequests = value.getUint()) { + assert(*maximumConcurrentRequests < std::numeric_limits<uint32_t>::max()); + const uint32_t maxConcurretnRequests = static_cast<uint32_t>(*maximumConcurrentRequests); + thread->actor().invoke(&OnlineFileSourceThread::setMaximumConcurrentRequests, maxConcurretnRequests); + { + std::lock_guard<std::mutex> lock(maximumConcurrentRequestsMutex); + cachedMaximumConcurrentRequests = maxConcurretnRequests; + } + } else { + Log::Error(Event::General, "Invalid max-concurrent-requests property value type."); + } + } - case Resource::Kind::SpriteImage: - case Resource::Kind::SpriteJSON: - res.url = util::mapbox::normalizeSpriteURL(apiBaseURL, resource.url, accessToken); - break; + uint32_t getMaximumConcurrentRequests() const { + std::lock_guard<std::mutex> lock(maximumConcurrentRequestsMutex); + return cachedMaximumConcurrentRequests; + } - case Resource::Kind::Tile: - res.url = util::mapbox::normalizeTileURL(apiBaseURL, resource.url, accessToken); - break; + void setAccessToken(const mapbox::base::Value& value) { + if (auto* accessToken = value.getString()) { + thread->actor().invoke(&OnlineFileSourceThread::setAccessToken, *accessToken); + { + std::lock_guard<std::mutex> lock(cachedAccessTokenMutex); + cachedAccessToken = *accessToken; + } + } else { + Log::Error(Event::General, "Invalid access-token property value type."); + } } - return std::make_unique<OnlineFileRequest>(std::move(res), std::move(callback), *impl); -} + std::string getAccessToken() const { + std::lock_guard<std::mutex> lock(cachedAccessTokenMutex); + return cachedAccessToken; + } -void OnlineFileSource::setResourceTransform(optional<ActorRef<ResourceTransform>>&& transform) { - impl->setResourceTransform(std::move(transform)); -} +private: + mutable std::mutex cachedAccessTokenMutex; + std::string cachedAccessToken; + mutable std::mutex cachedBaseURLMutex; + std::string cachedBaseURL = util::API_BASE_URL; + mutable std::mutex maximumConcurrentRequestsMutex; + uint32_t cachedMaximumConcurrentRequests = util::DEFAULT_MAXIMUM_CONCURRENT_REQUESTS; + const std::unique_ptr<util::Thread<OnlineFileSourceThread>> thread; +}; -OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSource::Impl& impl_) - : impl(impl_), - resource(std::move(resource_)), - callback(std::move(callback_)) { +OnlineFileRequest::OnlineFileRequest(Resource resource_, Callback callback_, OnlineFileSourceThread& impl_) + : impl(impl_), resource(std::move(resource_)), callback(std::move(callback_)) { impl.add(this); } @@ -337,12 +391,12 @@ void OnlineFileRequest::schedule() { } OnlineFileRequest::~OnlineFileRequest() { - impl.remove(this); + if (mailbox) { + mailbox->close(); + } } -Timestamp interpolateExpiration(const Timestamp& current, - optional<Timestamp> prior, - bool& expired) { +Timestamp interpolateExpiration(const Timestamp& current, optional<Timestamp> prior, bool& expired) { auto now = util::now(); if (current > now) { return current; @@ -383,9 +437,8 @@ void OnlineFileRequest::schedule(optional<Timestamp> expires) { // If we're not being asked for a forced refresh, calculate a timeout that depends on how many // consecutive errors we've encountered, and on the expiration time, if present. - Duration timeout = std::min( - http::errorRetryTimeout(failedRequestReason, failedRequests, retryAfter), - http::expirationTimeout(expires, expiredRequests)); + Duration timeout = std::min(http::errorRetryTimeout(failedRequestReason, failedRequests, retryAfter), + http::expirationTimeout(expires, expiredRequests)); if (timeout == Duration::max()) { return; @@ -469,7 +522,7 @@ void OnlineFileRequest::networkIsReachableAgain() { } } -void OnlineFileRequest::setTransformedURL(const std::string&& url) { +void OnlineFileRequest::setTransformedURL(const std::string& url) { resource.url = url; schedule(); } @@ -484,19 +537,95 @@ ActorRef<OnlineFileRequest> OnlineFileRequest::actor() { return ActorRef<OnlineFileRequest>(*this, mailbox); } -void OnlineFileSource::setMaximumConcurrentRequests(uint32_t maximumConcurrentRequests_) { - impl->setMaximumConcurrentRequests(maximumConcurrentRequests_); +void OnlineFileRequest::onCancel(std::function<void()> callback_) { + cancelCallback = std::move(callback_); +} + +OnlineFileSource::OnlineFileSource() : impl(std::make_unique<Impl>()) {} + +OnlineFileSource::~OnlineFileSource() = default; + +std::unique_ptr<AsyncRequest> OnlineFileSource::request(const Resource& resource, Callback callback) { + Resource res = resource; + + switch (resource.kind) { + case Resource::Kind::Unknown: + case Resource::Kind::Image: + break; + + case Resource::Kind::Style: + res.url = + mbgl::util::mapbox::normalizeStyleURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken()); + break; + + case Resource::Kind::Source: + res.url = util::mapbox::normalizeSourceURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken()); + break; + + case Resource::Kind::Glyphs: + res.url = util::mapbox::normalizeGlyphsURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken()); + break; + + case Resource::Kind::SpriteImage: + case Resource::Kind::SpriteJSON: + res.url = util::mapbox::normalizeSpriteURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken()); + break; + + case Resource::Kind::Tile: + res.url = util::mapbox::normalizeTileURL(impl->getAPIBaseURL(), resource.url, impl->getAccessToken()); + break; + } + + return impl->request(std::move(callback), std::move(res)); +} + +bool OnlineFileSource::canRequest(const Resource& resource) const { + return resource.hasLoadingMethod(Resource::LoadingMethod::Network) && + resource.url.rfind(mbgl::util::ASSET_PROTOCOL, 0) == std::string::npos && + resource.url.rfind(mbgl::util::FILE_PROTOCOL, 0) == std::string::npos; } -uint32_t OnlineFileSource::getMaximumConcurrentRequests() const { - return impl->getMaximumConcurrentRequests(); +void OnlineFileSource::pause() { + impl->pause(); } +void OnlineFileSource::resume() { + impl->resume(); +} + +void OnlineFileSource::setProperty(const std::string& key, const mapbox::base::Value& value) { + if (key == ACCESS_TOKEN_KEY) { + impl->setAccessToken(value); + } else if (key == API_BASE_URL_KEY) { + impl->setAPIBaseURL(value); + } else if (key == MAX_CONCURRENT_REQUESTS_KEY) { + impl->setMaximumConcurrentRequests(value); + } else if (key == ONLINE_STATUS_KEY) { + // For testing only + if (auto* boolValue = value.getBool()) { + impl->setOnlineStatus(*boolValue); + } + } else { + std::string message = "Resource provider does not support property " + key; + Log::Error(Event::General, message.c_str()); + } +} -// For testing only: +mapbox::base::Value OnlineFileSource::getProperty(const std::string& key) const { + if (key == ACCESS_TOKEN_KEY) { + return impl->getAccessToken(); + } else if (key == API_BASE_URL_KEY) { + return impl->getAPIBaseURL(); + } else if (key == MAX_CONCURRENT_REQUESTS_KEY) { + return impl->getMaximumConcurrentRequests(); + } + std::string message = "Resource provider does not support property " + key; + Log::Error(Event::General, message.c_str()); + return {}; +} -void OnlineFileSource::setOnlineStatus(const bool status) { - impl->setOnlineStatus(status); +void OnlineFileSource::setResourceTransform(ResourceTransform transform) { + impl->setResourceTransform(std::move(transform)); } } // namespace mbgl diff --git a/platform/glfw/main.cpp b/platform/glfw/main.cpp index 8f134804f0..ded8ee3e1f 100644 --- a/platform/glfw/main.cpp +++ b/platform/glfw/main.cpp @@ -3,13 +3,14 @@ #include "settings_json.hpp" #include <mbgl/gfx/backend.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/storage/database_file_source.hpp> +#include <mbgl/storage/file_source_manager.hpp> +#include <mbgl/style/style.hpp> #include <mbgl/util/default_styles.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/platform.hpp> #include <mbgl/util/string.hpp> -#include <mbgl/storage/default_file_source.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/renderer/renderer.hpp> #include <args.hxx> @@ -106,10 +107,16 @@ int main(int argc, char *argv[]) { mbgl::ResourceOptions resourceOptions; resourceOptions.withCachePath(cacheDB).withAccessToken(token); - auto fileSource = std::static_pointer_cast<mbgl::DefaultFileSource>(mbgl::FileSource::getSharedFileSource(resourceOptions)); + auto onlineFileSource = + mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Network, resourceOptions); if (!settings.online) { - fileSource->setOnlineStatus(false); - mbgl::Log::Warning(mbgl::Event::Setup, "Application is offline. Press `O` to toggle online status."); + if (onlineFileSource) { + onlineFileSource->setProperty("online-status", false); + mbgl::Log::Warning(mbgl::Event::Setup, "Application is offline. Press `O` to toggle online status."); + } else { + mbgl::Log::Warning(mbgl::Event::Setup, + "Network resource provider is not available, only local requests are supported."); + } } GLFWRendererFrontend rendererFrontend { std::make_unique<mbgl::Renderer>(view->getRendererBackend(), view->getPixelRatio()), *view }; @@ -132,9 +139,14 @@ int main(int argc, char *argv[]) { if (testDirValue) view->setTestDirectory(args::get(testDirValue)); - view->setOnlineStatusCallback([&settings, fileSource]() { + view->setOnlineStatusCallback([&settings, onlineFileSource]() { + if (!onlineFileSource) { + mbgl::Log::Warning(mbgl::Event::Setup, + "Cannot change online status. Network resource provider is not available."); + return; + } settings.online = !settings.online; - fileSource->setOnlineStatus(settings.online); + onlineFileSource->setProperty("online-status", settings.online); mbgl::Log::Info(mbgl::Event::Setup, "Application is %s. Press `O` to toggle online status.", settings.online ? "online" : "offline"); }); @@ -152,20 +164,26 @@ int main(int argc, char *argv[]) { mbgl::Log::Info(mbgl::Event::Setup, "Changed style to: %s", newStyle.name); }); - view->setPauseResumeCallback([fileSource] () { + // Resource loader controls top-level request processing and can resume / pause all managed sources simultaneously. + auto resourceLoader = + mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::ResourceLoader, resourceOptions); + view->setPauseResumeCallback([resourceLoader]() { static bool isPaused = false; if (isPaused) { - fileSource->resume(); + resourceLoader->resume(); } else { - fileSource->pause(); + resourceLoader->pause(); } isPaused = !isPaused; }); - view->setResetCacheCallback([fileSource] () { - fileSource->resetDatabase([](std::exception_ptr ex) { + // Database file source. + auto databaseFileSource = std::static_pointer_cast<mbgl::DatabaseFileSource>( + mbgl::FileSourceManager::get()->getFileSource(mbgl::FileSourceType::Database, resourceOptions)); + view->setResetCacheCallback([databaseFileSource]() { + databaseFileSource->resetDatabase([](std::exception_ptr ex) { if (ex) { mbgl::Log::Error(mbgl::Event::Database, "Failed to reset cache:: %s", mbgl::util::toString(ex).c_str()); } diff --git a/platform/linux/filesource-files.json b/platform/linux/filesource-files.json index 448f5f8613..669a4e612c 100644 --- a/platform/linux/filesource-files.json +++ b/platform/linux/filesource-files.json @@ -1,7 +1,6 @@ { "//": "This file can be edited manually and is the canonical source.", "sources": [ - "platform/default/src/mbgl/storage/file_source.cpp", "platform/default/src/mbgl/storage/http_file_source.cpp", "platform/default/src/mbgl/storage/sqlite3.cpp" ], |