summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--gyp/http-curl.gypi43
-rw-r--r--gyp/http-nsurl.gypi10
-rw-r--r--include/mbgl/map/map.hpp14
-rw-r--r--include/mbgl/storage/default/request.hpp8
-rw-r--r--include/mbgl/storage/default/shared_request_base.hpp35
-rw-r--r--include/mbgl/storage/default_file_source.hpp17
-rw-r--r--include/mbgl/storage/file_source.hpp12
-rw-r--r--platform/darwin/http_request_nsurl.mm3
-rw-r--r--src/mbgl/map/environment.cpp39
-rw-r--r--src/mbgl/map/environment.hpp44
-rw-r--r--src/mbgl/map/map.cpp40
-rw-r--r--src/mbgl/map/raster_tile_data.cpp7
-rw-r--r--src/mbgl/map/raster_tile_data.hpp2
-rw-r--r--src/mbgl/map/source.cpp47
-rw-r--r--src/mbgl/map/source.hpp22
-rw-r--r--src/mbgl/map/sprite.cpp22
-rw-r--r--src/mbgl/map/sprite.hpp7
-rw-r--r--src/mbgl/map/tile_data.cpp15
-rw-r--r--src/mbgl/map/tile_data.hpp8
-rw-r--r--src/mbgl/map/vector_tile_data.cpp4
-rw-r--r--src/mbgl/map/vector_tile_data.hpp7
-rw-r--r--src/mbgl/storage/default_file_source.cpp62
-rw-r--r--src/mbgl/storage/request.cpp4
-rw-r--r--src/mbgl/text/glyph_store.cpp15
-rw-r--r--src/mbgl/text/glyph_store.hpp10
-rw-r--r--test/storage/cache_response.cpp5
-rw-r--r--test/storage/cache_revalidate.cpp17
-rw-r--r--test/storage/directory_reading.cpp7
-rw-r--r--test/storage/file_reading.cpp17
-rw-r--r--test/storage/http_cancel.cpp14
-rw-r--r--test/storage/http_coalescing.cpp4
-rw-r--r--test/storage/http_environment.cpp54
-rw-r--r--test/storage/http_error.cpp18
-rw-r--r--test/storage/http_header_parsing.cpp10
-rw-r--r--test/storage/http_load.cpp6
-rw-r--r--test/storage/http_noloop.cpp5
-rw-r--r--test/storage/http_other_loop.cpp5
-rw-r--r--test/storage/http_reading.cpp17
-rwxr-xr-xtest/storage/server.js7
-rw-r--r--test/test.gyp1
41 files changed, 485 insertions, 201 deletions
diff --git a/.clang-format b/.clang-format
index 86d562e74e..109b562b59 100644
--- a/.clang-format
+++ b/.clang-format
@@ -2,7 +2,7 @@ Standard: Cpp11
IndentWidth: 4
AccessModifierOffset: -4
UseTab: Never
-BinPackParameters: false
+BinPackParameters: true
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortBlocksOnASingleLine: false
diff --git a/gyp/http-curl.gypi b/gyp/http-curl.gypi
index bfb2054d55..c97ad370b5 100644
--- a/gyp/http-curl.gypi
+++ b/gyp/http-curl.gypi
@@ -14,6 +14,25 @@
'../include',
],
+ 'variables': {
+ 'cflags_cc': [
+ '<@(uv_cflags)',
+ '<@(curl_cflags)',
+ '<@(boost_cflags)',
+ ],
+ 'ldflags': [
+ '<@(uv_ldflags)',
+ '<@(curl_ldflags)',
+ ],
+ 'libraries': [
+ '<@(uv_static_libs)',
+ '<@(curl_static_libs)',
+ ],
+ 'defines': [
+ '-DMBGL_HTTP_CURL'
+ ],
+ },
+
'conditions': [
['host == "android"', {
'variables': {
@@ -32,19 +51,17 @@
}],
],
- 'variables': {
- 'cflags_cc': [
- '<@(uv_cflags)',
- '<@(curl_cflags)',
- '<@(boost_cflags)',
- ],
- 'ldflags': [
- '<@(uv_ldflags)',
- '<@(curl_ldflags)',
- ],
- 'libraries': [
- '<@(uv_static_libs)',
- '<@(curl_static_libs)',
+ 'direct_dependent_settings': {
+ 'conditions': [
+ ['OS == "mac"', {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [ '<@(defines)' ],
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
+ }
+ }, {
+ 'cflags': [ '<@(defines)' ],
+ 'cflags_cc': [ '<@(defines)' ],
+ }]
],
},
diff --git a/gyp/http-nsurl.gypi b/gyp/http-nsurl.gypi
index 4205f59d81..5a079fdeeb 100644
--- a/gyp/http-nsurl.gypi
+++ b/gyp/http-nsurl.gypi
@@ -25,6 +25,9 @@
'libraries': [
'<@(uv_static_libs)',
],
+ 'defines': [
+ '-DMBGL_HTTP_NSURL'
+ ],
},
'xcode_settings': {
@@ -32,6 +35,13 @@
'CLANG_ENABLE_OBJC_ARC': 'NO',
},
+ 'direct_dependent_settings': {
+ 'xcode_settings': {
+ 'OTHER_CFLAGS': [ '<@(defines)' ],
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
+ }
+ },
+
'link_settings': {
'libraries': [ '<@(libraries)' ],
'xcode_settings': {
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 8711aa3d08..00719aa382 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -36,11 +36,7 @@ class View;
class GlyphAtlas;
class SpriteAtlas;
class LineAtlas;
-
-struct exception : std::runtime_error {
- inline exception(const char *msg) : std::runtime_error(msg) {
- }
-};
+class Environment;
class Map : private util::noncopyable {
friend class View;
@@ -182,8 +178,8 @@ private:
Mode mode = Mode::None;
-public: // TODO: make private again
- std::unique_ptr<uv::loop> loop;
+ const std::unique_ptr<Environment> env;
+ View &view;
private:
std::unique_ptr<uv::worker> workers;
@@ -214,12 +210,8 @@ private:
// Stores whether the map thread has been stopped already.
std::atomic_bool isStopped;
- View &view;
-
-#ifdef DEBUG
const std::thread::id mainThread;
std::thread::id mapThread;
-#endif
Transform transform;
TransformState state;
diff --git a/include/mbgl/storage/default/request.hpp b/include/mbgl/storage/default/request.hpp
index 648585f304..b686d1fe90 100644
--- a/include/mbgl/storage/default/request.hpp
+++ b/include/mbgl/storage/default/request.hpp
@@ -15,13 +15,14 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Response;
+class Environment;
class Request : private util::noncopyable {
MBGL_STORE_THREAD(tid)
public:
using Callback = std::function<void(const Response &)>;
- Request(const Resource &resource, uv_loop_t *loop, Callback callback);
+ Request(const Resource &resource, uv_loop_t *loop, const Environment &env, Callback callback);
public:
// May be called from any thread.
@@ -45,6 +46,11 @@ private:
public:
const Resource resource;
+
+ // The environment ref is used to associate requests with a particular environment. This allows
+ // us to only terminate requests associated with that environment, e.g. when the map the env
+ // belongs to is discarded.
+ const Environment &env;
};
}
diff --git a/include/mbgl/storage/default/shared_request_base.hpp b/include/mbgl/storage/default/shared_request_base.hpp
index 2d56615608..59e38efc2f 100644
--- a/include/mbgl/storage/default/shared_request_base.hpp
+++ b/include/mbgl/storage/default/shared_request_base.hpp
@@ -4,11 +4,13 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/file_cache.hpp>
#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/default/request.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <string>
#include <set>
+#include <vector>
#include <cassert>
typedef struct uv_loop_s uv_loop_t;
@@ -45,20 +47,12 @@ public:
observers.insert(request);
}
- void unsubscribeAll() {
- MBGL_VERIFY_THREAD(tid);
-
- source = nullptr;
- observers.clear();
- cancel();
- }
-
void unsubscribe(Request *request) {
MBGL_VERIFY_THREAD(tid);
observers.erase(request);
- if (observers.empty()) {
+ if (abandoned()) {
// There are no observers anymore. We are initiating cancelation.
if (source) {
// First, remove this SharedRequestBase from the source.
@@ -70,6 +64,29 @@ public:
}
}
+ bool abandoned() const {
+ return observers.empty();
+ }
+
+ std::vector<Request *> removeAllInEnvironment(const Environment &env) {
+ MBGL_VERIFY_THREAD(tid);
+
+ std::vector<Request *> result;
+
+ // Removes all Requests in the supplied environment and returns a list
+ // of them.
+ util::erase_if(observers, [&](Request *req) -> bool {
+ if (&req->env == &env) {
+ result.push_back(req);
+ return true;
+ } else {
+ return false;
+ }
+ });
+
+ return result;
+ }
+
protected:
virtual ~SharedRequestBase() {
MBGL_VERIFY_THREAD(tid);
diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp
index 86e2414041..14b4db6eff 100644
--- a/include/mbgl/storage/default_file_source.hpp
+++ b/include/mbgl/storage/default_file_source.hpp
@@ -20,11 +20,14 @@ class DefaultFileSource : public FileSource {
public:
DefaultFileSource(FileCache *cache, const std::string &root = "");
DefaultFileSource(FileCache *cache, uv_loop_t *loop, const std::string &root = "");
- ~DefaultFileSource();
+ ~DefaultFileSource() override;
- Request *request(const Resource &resource, uv_loop_t *loop, Callback callback);
- void cancel(Request *request);
- void request(const Resource &resource, Callback callback);
+ Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env,
+ Callback callback) override;
+ void cancel(Request *request) override;
+ void request(const Resource &resource, const Environment &env, Callback callback) override;
+
+ void abort(const Environment &env) override;
enum class CacheHint : uint8_t { Full, Refresh, No };
void notify(SharedRequestBase *sharedRequest, const std::set<Request *> &observers,
@@ -39,14 +42,16 @@ private:
struct RemoveRequestAction;
struct ResultAction;
struct StopAction;
- using Action =
- mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction, StopAction>;
+ struct AbortAction;
+ using Action = mapbox::util::variant<AddRequestAction, RemoveRequestAction, ResultAction,
+ StopAction, AbortAction>;
using Queue = util::AsyncQueue<Action>;
void process(AddRequestAction &action);
void process(RemoveRequestAction &action);
void process(ResultAction &action);
void process(StopAction &action);
+ void process(AbortAction &action);
SharedRequestBase *find(const Resource &resource);
diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp
index 8517d6e4a6..30e88c39f6 100644
--- a/include/mbgl/storage/file_source.hpp
+++ b/include/mbgl/storage/file_source.hpp
@@ -15,6 +15,7 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Request;
+class Environment;
class FileSource : private util::noncopyable {
protected:
@@ -27,12 +28,19 @@ public:
// These can be called from any thread. The callback will be invoked in the loop.
// You can only cancel a request from the same thread it was created in.
- virtual Request *request(const Resource &resource, uv_loop_t *loop, Callback callback) = 0;
+ virtual Request *request(const Resource &resource, uv_loop_t *loop, const Environment &env,
+ Callback callback) = 0;
virtual void cancel(Request *request) = 0;
// These can be called from any thread. The callback will be invoked in an arbitrary other thread.
// You cannot cancel these requests.
- virtual void request(const Resource &resource, Callback callback) = 0;
+ virtual void request(const Resource &resource, const Environment &env, Callback callback) = 0;
+
+ // This can be called from any thread. All requests with the environment pointer env should be
+ // notified as errored. Note that this is /different/ from canceling requests; a canceled
+ // request's callback is never called, while an aborted request's callback is called with
+ // a error message.
+ virtual void abort(const Environment &env) = 0;
};
}
diff --git a/platform/darwin/http_request_nsurl.mm b/platform/darwin/http_request_nsurl.mm
index 83c010f8b8..638b5062d4 100644
--- a/platform/darwin/http_request_nsurl.mm
+++ b/platform/darwin/http_request_nsurl.mm
@@ -188,6 +188,7 @@ void HTTPRequestImpl::handleResponse() {
}
context->removeRequest(request);
+ request->ptr = nullptr;
delete request;
request = nullptr;
}
@@ -206,6 +207,8 @@ void HTTPRequestImpl::cancel() {
[task cancel];
[task release];
task = nullptr;
+ } else {
+ delete this;
}
}
diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp
new file mode 100644
index 0000000000..ee13d33ea7
--- /dev/null
+++ b/src/mbgl/map/environment.cpp
@@ -0,0 +1,39 @@
+#include <mbgl/map/environment.hpp>
+#include <mbgl/storage/file_source.hpp>
+
+#include <uv.h>
+
+#include <cassert>
+
+namespace mbgl {
+
+Environment::Environment(FileSource &fs) : fileSource(fs), loop(uv_loop_new()) {
+}
+
+void Environment::setup() {
+ mapThread = std::this_thread::get_id();
+}
+
+bool Environment::inMapThread() const {
+ return std::this_thread::get_id() == mapThread;
+}
+
+void Environment::requestAsync(const Resource &resource, std::function<void(const Response &)> callback) {
+ fileSource.request(resource, *this, std::move(callback));
+}
+
+Request *Environment::request(const Resource &resource, std::function<void(const Response &)> callback) {
+ assert(inMapThread());
+ return fileSource.request(resource, loop, *this, std::move(callback));
+}
+
+void Environment::cancelRequest(Request *req) {
+ assert(inMapThread());
+ fileSource.cancel(req);
+}
+
+void Environment::terminate() {
+ fileSource.abort(*this);
+}
+
+}
diff --git a/src/mbgl/map/environment.hpp b/src/mbgl/map/environment.hpp
new file mode 100644
index 0000000000..b68a7b9e2f
--- /dev/null
+++ b/src/mbgl/map/environment.hpp
@@ -0,0 +1,44 @@
+#ifndef MBGL_MAP_MAP_ENVIRONMENT
+#define MBGL_MAP_MAP_ENVIRONMENT
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/util.hpp>
+
+#include <thread>
+#include <functional>
+
+typedef struct uv_loop_s uv_loop_t;
+
+namespace mbgl {
+
+class FileSource;
+class Request;
+class Response;
+struct Resource;
+
+class Environment : private util::noncopyable {
+public:
+ Environment(FileSource &);
+
+ void setup();
+
+ bool inMapThread() const;
+
+ void requestAsync(const Resource &, std::function<void(const Response &)>);
+ Request *request(const Resource &, std::function<void(const Response &)>);
+ void cancelRequest(Request *);
+
+ // Request to terminate the environment.
+ void terminate();
+
+private:
+ FileSource &fileSource;
+ std::thread::id mapThread;
+
+public:
+ uv_loop_t *const loop;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 518bc5bb96..0cd7d3621e 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -1,4 +1,5 @@
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/map/view.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/map/source.hpp>
@@ -25,6 +26,7 @@
#include <mbgl/util/string.hpp>
#include <mbgl/util/uv.hpp>
#include <mbgl/util/mapbox.hpp>
+#include <mbgl/util/exception.hpp>
#include <algorithm>
#include <iostream>
@@ -57,16 +59,14 @@ const static bool uvVersionCheck = []() {
using namespace mbgl;
Map::Map(View& view_, FileSource& fileSource_)
- : loop(util::make_unique<uv::loop>()),
+ : env(util::make_unique<Environment>(fileSource_)),
view(view_),
-#ifdef DEBUG
mainThread(std::this_thread::get_id()),
mapThread(mainThread),
-#endif
transform(view_),
fileSource(fileSource_),
glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
- glyphStore(std::make_shared<GlyphStore>(fileSource)),
+ glyphStore(std::make_shared<GlyphStore>(*env)),
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
texturePool(std::make_shared<TexturePool>()),
@@ -92,7 +92,7 @@ Map::~Map() {
texturePool.reset();
workers.reset();
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
}
uv::worker &Map::getWorker() {
@@ -112,7 +112,7 @@ void Map::start(bool startPaused) {
isStopped = false;
// Setup async notifications
- asyncTerminate = util::make_unique<uv::async>(**loop, [this]() {
+ asyncTerminate = util::make_unique<uv::async>(env->loop, [this]() {
assert(std::this_thread::get_id() == mapThread);
// Remove all of these to make sure they are destructed in the correct thread.
@@ -127,7 +127,7 @@ void Map::start(bool startPaused) {
asyncTerminate.reset();
});
- asyncRender = util::make_unique<uv::async>(**loop, [this]() {
+ asyncRender = util::make_unique<uv::async>(env->loop, [this]() {
assert(std::this_thread::get_id() == mapThread);
if (state.hasSize()) {
@@ -207,7 +207,7 @@ void Map::pause(bool waitForPause) {
pausing = true;
mutexRun.unlock();
- uv_stop(**loop);
+ uv_stop(env->loop);
rerender(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
if (waitForPause) {
@@ -242,12 +242,14 @@ void Map::run() {
}
if (mode == Mode::Static && !style && styleURL.empty()) {
- throw exception("Style is not set");
+ throw util::Exception("Style is not set");
}
view.activate();
- workers = util::make_unique<uv::worker>(**loop, 4, "Tile Worker");
+ workers = util::make_unique<uv::worker>(env->loop, 4, "Tile Worker");
+
+ env->setup();
setup();
prepare();
@@ -255,15 +257,15 @@ void Map::run() {
if (mode == Mode::Continuous) {
terminating = false;
while(!terminating) {
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
checkForPause();
}
} else {
- uv_run(**loop, UV_RUN_DEFAULT);
+ uv_run(env->loop, UV_RUN_DEFAULT);
}
// Run the event loop once more to make sure our async delete handlers are called.
- uv_run(**loop, UV_RUN_ONCE);
+ uv_run(env->loop, UV_RUN_ONCE);
// If the map rendering wasn't started asynchronously, we perform one render
// *after* all events have been processed.
@@ -376,7 +378,7 @@ util::ptr<Sprite> Map::getSprite() {
const float pixelRatio = state.getPixelRatio();
const std::string &sprite_url = style->getSpriteURL();
if (!sprite || sprite->pixelRatio != pixelRatio) {
- sprite = Sprite::Create(sprite_url, pixelRatio, fileSource);
+ sprite = Sprite::Create(sprite_url, pixelRatio, *env);
}
return sprite;
@@ -621,7 +623,7 @@ void Map::updateSources() {
if (source->enabled) {
if (!source->source) {
source->source = std::make_shared<Source>(source->info);
- source->source->load(*this, fileSource);
+ source->source->load(*this, *env);
}
} else {
source->source.reset();
@@ -648,10 +650,8 @@ void Map::updateSources(const util::ptr<StyleLayerGroup> &group) {
void Map::updateTiles() {
for (const auto& source : activeSources) {
- source->source->update(*this, getWorker(),
- style, *glyphAtlas, *glyphStore,
- *spriteAtlas, getSprite(),
- *texturePool, fileSource, ***loop, [this](){ update(); });
+ source->source->update(*this, *env, getWorker(), style, *glyphAtlas, *glyphStore,
+ *spriteAtlas, getSprite(), *texturePool, [this]() { update(); });
}
}
@@ -659,7 +659,7 @@ void Map::prepare() {
if (!style) {
style = std::make_shared<Style>();
- fileSource.request({ Resource::Kind::JSON, styleURL}, **loop, [&](const Response &res) {
+ env->request({ Resource::Kind::JSON, styleURL}, [&](const Response &res) {
if (res.status == Response::Successful) {
// Calculate the base
const size_t pos = styleURL.rfind('/');
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index 1ca9ef8041..4cd7fc2b5e 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -4,10 +4,9 @@
using namespace mbgl;
-
-RasterTileData::RasterTileData(Tile::ID const& id_, TexturePool& texturePool, const SourceInfo& source_, FileSource& fileSource_)
- : TileData(id_, source_, fileSource_),
- bucket(texturePool, layout) {
+RasterTileData::RasterTileData(Tile::ID const &id_, TexturePool &texturePool,
+ const SourceInfo &source_, Environment &env_)
+ : TileData(id_, source_, env_), bucket(texturePool, layout) {
}
RasterTileData::~RasterTileData() {
diff --git a/src/mbgl/map/raster_tile_data.hpp b/src/mbgl/map/raster_tile_data.hpp
index 9a8578a61e..2413e13fb0 100644
--- a/src/mbgl/map/raster_tile_data.hpp
+++ b/src/mbgl/map/raster_tile_data.hpp
@@ -17,7 +17,7 @@ class RasterTileData : public TileData {
friend class TileParser;
public:
- RasterTileData(Tile::ID const& id, TexturePool&, const SourceInfo&, FileSource &);
+ RasterTileData(Tile::ID const &id, TexturePool &, const SourceInfo &, Environment &);
~RasterTileData();
void parse() override;
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index d9236b19b3..ec3a1b8e00 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -1,12 +1,14 @@
#include <mbgl/map/source.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/map/transform.hpp>
#include <mbgl/renderer/painter.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/raster.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/texture_pool.hpp>
-#include <mbgl/storage/file_source.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/std.hpp>
@@ -32,7 +34,7 @@ Source::Source(SourceInfo& info_)
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Source::load(Map& map, FileSource& fileSource) {
+void Source::load(Map &map, Environment &env) {
if (info.url.empty()) {
loaded = true;
return;
@@ -41,7 +43,7 @@ void Source::load(Map& map, FileSource& fileSource) {
util::ptr<Source> source = shared_from_this();
const std::string url = util::mapbox::normalizeSourceURL(info.url, map.getAccessToken());
- fileSource.request({ Resource::Kind::JSON, url }, **map.loop, [source, &map](const Response &res) {
+ env.request({ Resource::Kind::JSON, url }, [source, &map](const Response &res) {
if (res.status != Response::Successful) {
Log::Warning(Event::General, "Failed to load source TileJSON: %s", res.message.c_str());
return;
@@ -153,13 +155,11 @@ TileData::State Source::hasTile(const Tile::ID& id) {
return TileData::State::invalid;
}
-TileData::State Source::addTile(Map& map, uv::worker& worker,
- util::ptr<Style> style,
- GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
- SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
- FileSource& fileSource, uv_loop_t &loop, TexturePool& texturePool,
- const Tile::ID& id,
- std::function<void ()> callback) {
+TileData::State Source::addTile(Map &map, Environment &env, uv::worker &worker,
+ util::ptr<Style> style, GlyphAtlas &glyphAtlas,
+ GlyphStore &glyphStore, SpriteAtlas &spriteAtlas,
+ util::ptr<Sprite> sprite, TexturePool &texturePool,
+ const Tile::ID &id, std::function<void()> callback) {
const TileData::State state = hasTile(id);
if (state != TileData::State::invalid) {
@@ -190,14 +190,14 @@ TileData::State Source::addTile(Map& map, uv::worker& worker,
new_tile.data = std::make_shared<VectorTileData>(normalized_id, map.getMaxZoom(), style,
glyphAtlas, glyphStore,
spriteAtlas, sprite,
- info, fileSource);
+ info, env);
} else if (info.type == SourceType::Raster) {
- new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, fileSource);
+ new_tile.data = std::make_shared<RasterTileData>(normalized_id, texturePool, info, env);
} else {
throw std::runtime_error("source type not implemented");
}
- new_tile.data->request(worker, loop, map.getState().getPixelRatio(), callback);
+ new_tile.data->request(worker, map.getState().getPixelRatio(), callback);
tile_data.emplace(new_tile.data->id, new_tile.data);
}
@@ -283,12 +283,16 @@ bool Source::findLoadedParent(const Tile::ID& id, int32_t minCoveringZoom, std::
return false;
}
-void Source::update(Map& map, uv::worker& worker,
+void Source::update(Map &map,
+ Environment &env,
+ uv::worker &worker,
util::ptr<Style> style,
- GlyphAtlas& glyphAtlas, GlyphStore& glyphStore,
- SpriteAtlas& spriteAtlas, util::ptr<Sprite> sprite,
- TexturePool& texturePool, FileSource& fileSource, uv_loop_t& loop,
- std::function<void ()> callback) {
+ GlyphAtlas &glyphAtlas,
+ GlyphStore &glyphStore,
+ SpriteAtlas &spriteAtlas,
+ util::ptr<Sprite> sprite,
+ TexturePool &texturePool,
+ std::function<void()> callback) {
if (!loaded || map.getTime() <= updated)
return;
@@ -308,11 +312,8 @@ void Source::update(Map& map, uv::worker& worker,
// Add existing child/parent tiles if the actual tile is not yet loaded
for (const Tile::ID& id : required) {
- const TileData::State state = addTile(map, worker, style,
- glyphAtlas, glyphStore,
- spriteAtlas, sprite,
- fileSource, loop, texturePool,
- id, callback);
+ const TileData::State state = addTile(map, env, worker, style, glyphAtlas, glyphStore,
+ spriteAtlas, sprite, texturePool, id, callback);
if (state != TileData::State::parsed) {
// The tile we require is not yet loaded. Try to find a parent or
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index 2c0e444225..061908b89d 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -18,11 +18,11 @@
namespace mbgl {
class Map;
+class Environment;
class GlyphAtlas;
class GlyphStore;
class SpriteAtlas;
class Sprite;
-class FileSource;
class TexturePool;
class Style;
class Painter;
@@ -34,13 +34,9 @@ class Source : public std::enable_shared_from_this<Source>, private util::noncop
public:
Source(SourceInfo&);
- void load(Map&, FileSource&);
- void update(Map&, uv::worker&,
- util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- TexturePool&, FileSource&, uv_loop_t& loop,
- std::function<void ()> callback);
+ void load(Map &, Environment &);
+ void update(Map &, Environment &, uv::worker &, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
+ SpriteAtlas &, util::ptr<Sprite>, TexturePool &, std::function<void()> callback);
void updateMatrices(const mat4 &projMatrix, const TransformState &transform);
void drawClippingMasks(Painter &painter);
@@ -59,13 +55,9 @@ private:
int32_t coveringZoomLevel(const TransformState&) const;
std::forward_list<Tile::ID> coveringTiles(const TransformState&) const;
- TileData::State addTile(Map&, uv::worker&,
- util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- FileSource&, uv_loop_t &, TexturePool&,
- const Tile::ID&,
- std::function<void ()> callback);
+ TileData::State addTile(Map &, Environment &, uv::worker &, util::ptr<Style>, GlyphAtlas &,
+ GlyphStore &, SpriteAtlas &, util::ptr<Sprite>, TexturePool &,
+ const Tile::ID &, std::function<void()> callback);
TileData::State hasTile(const Tile::ID& id);
diff --git a/src/mbgl/map/sprite.cpp b/src/mbgl/map/sprite.cpp
index 9543fe083a..4be92ff73f 100644
--- a/src/mbgl/map/sprite.cpp
+++ b/src/mbgl/map/sprite.cpp
@@ -5,7 +5,9 @@
#include <string>
#include <mbgl/platform/platform.hpp>
-#include <mbgl/storage/file_source.hpp>
+#include <mbgl/map/environment.hpp>
+#include <mbgl/storage/resource.hpp>
+#include <mbgl/storage/response.hpp>
#include <mbgl/util/uv_detail.hpp>
#include <mbgl/util/std.hpp>
@@ -22,9 +24,9 @@ SpritePosition::SpritePosition(uint16_t x_, uint16_t y_, uint16_t width_, uint16
sdf(sdf_) {
}
-util::ptr<Sprite> Sprite::Create(const std::string& base_url, float pixelRatio, FileSource& fileSource) {
+util::ptr<Sprite> Sprite::Create(const std::string &base_url, float pixelRatio, Environment &env) {
util::ptr<Sprite> sprite(std::make_shared<Sprite>(Key(), base_url, pixelRatio));
- sprite->load(fileSource);
+ sprite->load(env);
return sprite;
}
@@ -51,7 +53,7 @@ Sprite::operator bool() const {
// Note: This is a separate function that must be called exactly once after creation
// The reason this isn't part of the constructor is that calling shared_from_this() in
// the constructor fails.
-void Sprite::load(FileSource& fileSource) {
+void Sprite::load(Environment &env) {
if (!valid) {
// Treat a non-existent sprite as a successfully loaded empty sprite.
@@ -63,29 +65,25 @@ void Sprite::load(FileSource& fileSource) {
util::ptr<Sprite> sprite = shared_from_this();
- fileSource.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
+ env.request({ Resource::Kind::JSON, jsonURL }, [sprite](const Response &res) {
if (res.status == Response::Successful) {
sprite->body = res.data;
sprite->parseJSON();
sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite info: %s", res.message.c_str());
- if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
- }
+ sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
});
- fileSource.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
+ env.request({ Resource::Kind::Image, spriteURL }, [sprite](const Response &res) {
if (res.status == Response::Successful) {
sprite->image = res.data;
sprite->parseImage();
sprite->complete();
} else {
Log::Warning(Event::Sprite, "Failed to load sprite image: %s", res.message.c_str());
- if (!sprite->future.valid()) {
- sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
- }
+ sprite->promise.set_exception(std::make_exception_ptr(std::runtime_error(res.message)));
}
});
}
diff --git a/src/mbgl/map/sprite.hpp b/src/mbgl/map/sprite.hpp
index d4b54ba1b5..cb0c274dee 100644
--- a/src/mbgl/map/sprite.hpp
+++ b/src/mbgl/map/sprite.hpp
@@ -14,7 +14,7 @@
namespace mbgl {
-class FileSource;
+class Environment;
class SpritePosition {
public:
@@ -34,11 +34,12 @@ public:
class Sprite : public std::enable_shared_from_this<Sprite>, private util::noncopyable {
private:
struct Key {};
- void load(FileSource& fileSource);
+ void load(Environment &env);
public:
Sprite(const Key &, const std::string& base_url, float pixelRatio);
- static util::ptr<Sprite> Create(const std::string& base_url, float pixelRatio, FileSource& fileSource);
+ static util::ptr<Sprite>
+ Create(const std::string &base_url, float pixelRatio, Environment &env);
const SpritePosition &getSpritePosition(const std::string& name) const;
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index 9d48041239..53d36dfe8b 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -1,5 +1,6 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/map/map.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/style/style_source.hpp>
#include <mbgl/util/token.hpp>
@@ -10,12 +11,12 @@
using namespace mbgl;
-TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& fileSource_)
+TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, Environment& env_)
: id(id_),
name(id),
state(State::initial),
source(source_),
- fileSource(fileSource_),
+ env(env_),
debugBucket(debugFontBuffer) {
// Initialize tile debug coordinates
debugFontBuffer.addText(name.c_str(), 50, 200, 5);
@@ -23,7 +24,7 @@ TileData::TileData(Tile::ID const& id_, const SourceInfo& source_, FileSource& f
TileData::~TileData() {
if (req) {
- fileSource.cancel(req);
+ env.cancelRequest(req);
}
}
@@ -31,8 +32,7 @@ const std::string TileData::toString() const {
return std::string { "[tile " } + name + "]";
}
-void TileData::request(uv::worker &worker, uv_loop_t &loop,
- float pixelRatio, std::function<void()> callback) {
+void TileData::request(uv::worker &worker, float pixelRatio, std::function<void()> callback) {
if (source.tiles.empty())
return;
@@ -53,9 +53,8 @@ void TileData::request(uv::worker &worker, uv_loop_t &loop,
state = State::loading;
- // Note: Somehow this feels slower than the change to request_http()
std::weak_ptr<TileData> weak_tile = shared_from_this();
- req = fileSource.request({ Resource::Kind::Tile, url }, &loop, [weak_tile, url, callback, &worker](const Response &res) {
+ req = env.request({ Resource::Kind::Tile, url }, [weak_tile, url, callback, &worker](const Response &res) {
util::ptr<TileData> tile = weak_tile.lock();
if (!tile || tile->state == State::obsolete) {
// noop. Tile is obsolete and we're now just waiting for the refcount
@@ -84,7 +83,7 @@ void TileData::cancel() {
state = State::obsolete;
}
if (req) {
- fileSource.cancel(req);
+ env.cancelRequest(req);
req = nullptr;
}
}
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index fcd741a1f8..66c0fbba2e 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -23,7 +23,7 @@ typedef struct uv_loop_s uv_loop_t;
namespace mbgl {
class Map;
-class FileSource;
+class Environment;
class Painter;
class SourceInfo;
class StyleLayer;
@@ -48,10 +48,10 @@ public:
};
public:
- TileData(Tile::ID const& id, const SourceInfo&, FileSource&);
+ TileData(Tile::ID const &id, const SourceInfo &, Environment &);
~TileData();
- void request(uv::worker&, uv_loop_t&, float pixelRatio, std::function<void ()> callback);
+ void request(uv::worker&, float pixelRatio, std::function<void ()> callback);
void reparse(uv::worker&, std::function<void ()> callback);
void cancel();
const std::string toString() const;
@@ -72,7 +72,7 @@ public:
public:
const SourceInfo& source;
- FileSource& fileSource;
+ Environment &env;
protected:
Request *req = nullptr;
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 68974aadf4..66fed0706b 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -14,8 +14,8 @@ VectorTileData::VectorTileData(Tile::ID const& id_,
float mapMaxZoom, util::ptr<Style> style_,
GlyphAtlas& glyphAtlas_, GlyphStore& glyphStore_,
SpriteAtlas& spriteAtlas_, util::ptr<Sprite> sprite_,
- const SourceInfo& source_, FileSource &fileSource_)
- : TileData(id_, source_, fileSource_),
+ const SourceInfo& source_, Environment &env_)
+ : TileData(id_, source_, env_),
glyphAtlas(glyphAtlas_),
glyphStore(glyphStore_),
spriteAtlas(spriteAtlas_),
diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp
index feb3c6238b..1c48fc5f23 100644
--- a/src/mbgl/map/vector_tile_data.hpp
+++ b/src/mbgl/map/vector_tile_data.hpp
@@ -30,11 +30,8 @@ class VectorTileData : public TileData {
friend class TileParser;
public:
- VectorTileData(Tile::ID const&,
- float mapMaxZoom, util::ptr<Style>,
- GlyphAtlas&, GlyphStore&,
- SpriteAtlas&, util::ptr<Sprite>,
- const SourceInfo&, FileSource &);
+ VectorTileData(Tile::ID const &, float mapMaxZoom, util::ptr<Style>, GlyphAtlas &, GlyphStore &,
+ SpriteAtlas &, util::ptr<Sprite>, const SourceInfo &, Environment &);
~VectorTileData();
void parse() override;
diff --git a/src/mbgl/storage/default_file_source.cpp b/src/mbgl/storage/default_file_source.cpp
index c6b201b559..9f70ac9943 100644
--- a/src/mbgl/storage/default_file_source.cpp
+++ b/src/mbgl/storage/default_file_source.cpp
@@ -50,6 +50,10 @@ struct DefaultFileSource::ResultAction {
struct DefaultFileSource::StopAction {
};
+struct DefaultFileSource::AbortAction {
+ const Environment &env;
+};
+
DefaultFileSource::DefaultFileSource(FileCache *cache_, const std::string &root)
: assetRoot(root.empty() ? platform::assetRoot() : root),
@@ -105,8 +109,9 @@ SharedRequestBase *DefaultFileSource::find(const Resource &resource) {
return nullptr;
}
-Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Callback callback) {
- auto req = new Request(resource, l, std::move(callback));
+Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, const Environment &env,
+ Callback callback) {
+ auto req = new Request(resource, l, env, std::move(callback));
// This function can be called from any thread. Make sure we're executing the actual call in the
// file source loop by sending it over the queue. It will be processed in processAction().
@@ -114,8 +119,9 @@ Request *DefaultFileSource::request(const Resource &resource, uv_loop_t *l, Call
return req;
}
-void DefaultFileSource::request(const Resource &resource, Callback callback) {
- auto req = new Request(resource, nullptr, std::move(callback));
+void DefaultFileSource::request(const Resource &resource, const Environment &env,
+ Callback callback) {
+ auto req = new Request(resource, nullptr, env, std::move(callback));
// This function can be called from any thread. Make sure we're executing the actual call in the
// file source loop by sending it over the queue. It will be processed in processAction().
@@ -130,6 +136,11 @@ void DefaultFileSource::cancel(Request *req) {
queue->send(RemoveRequestAction{ req });
}
+void DefaultFileSource::abort(const Environment &env) {
+ queue->send(AbortAction{ env });
+}
+
+
void DefaultFileSource::process(AddRequestAction &action) {
const Resource &resource = action.request->resource;
@@ -209,18 +220,45 @@ void DefaultFileSource::process(ResultAction &action) {
}
}
+// A stop action means the file source is about to be destructed. We need to cancel all requests
+// for all environments.
void DefaultFileSource::process(StopAction &) {
- // Cancel all remaining requests.
- for (auto it : pending) {
- it.second->unsubscribeAll();
- }
- pending.clear();
-
+ // There may not be any pending requests in this file source anymore. You must terminate all
+ // Map objects before deleting the FileSource.
+ assert(pending.empty());
assert(queue);
queue->stop();
queue = nullptr;
}
+// Aborts all requests that are part of the current environment.
+void DefaultFileSource::process(AbortAction &action) {
+ // Construct a cancellation response.
+ auto res = util::make_unique<Response>();
+ res->status = Response::Error;
+ res->message = "Environment is terminating";
+ std::shared_ptr<const Response> response = std::move(res);
+
+ // Iterate through all pending requests and remove them in case they're abandoned.
+ util::erase_if(pending, [&](const std::pair<Resource, SharedRequestBase *> &it) -> bool {
+ // Obtain all pending requests that are in the current environment.
+ const auto aborted = it.second->removeAllInEnvironment(action.env);
+
+ // Notify all observers.
+ for (auto req : aborted) {
+ req->notify(response);
+ }
+
+ // Finally, remove all requests that are now abandoned.
+ if (it.second->abandoned()) {
+ it.second->cancel();
+ return true;
+ } else {
+ return false;
+ }
+ });
+}
+
void DefaultFileSource::notify(SharedRequestBase *sharedRequest,
const std::set<Request *> &observers,
std::shared_ptr<const Response> response, FileCache::Hint hint) {
@@ -235,8 +273,8 @@ void DefaultFileSource::notify(SharedRequestBase *sharedRequest,
}
// Notify all observers.
- for (auto it : observers) {
- it->notify(response);
+ for (auto req : observers) {
+ req->notify(response);
}
}
diff --git a/src/mbgl/storage/request.cpp b/src/mbgl/storage/request.cpp
index de18138ec2..48b5b774d2 100644
--- a/src/mbgl/storage/request.cpp
+++ b/src/mbgl/storage/request.cpp
@@ -13,8 +13,8 @@
namespace mbgl {
// Note: This requires that loop is running in the current thread (or not yet running).
-Request::Request(const Resource &resource_, uv_loop_t *loop, Callback callback_)
- : callback(callback_), resource(resource_) {
+Request::Request(const Resource &resource_, uv_loop_t *loop, const Environment &env_, Callback callback_)
+ : callback(callback_), resource(resource_), env(env_) {
// When there is no loop supplied (== nullptr), the callback will be fired in an arbitrary
// thread (the thread notify() is called from) rather than kicking back to the calling thread.
if (loop) {
diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp
index f89f42e909..ab1776c04b 100644
--- a/src/mbgl/text/glyph_store.cpp
+++ b/src/mbgl/text/glyph_store.cpp
@@ -1,5 +1,6 @@
#include <mbgl/text/glyph_store.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/util/std.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/utf.hpp>
@@ -137,9 +138,11 @@ void FontStack::lineWrap(Shaping &shaping, const float lineHeight, const float m
align(shaping, justify, horizontalAlign, verticalAlign, maxLineLength, lineHeight, line);
}
-GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource)
- : future(promise.get_future().share())
-{
+GlyphPBF::GlyphPBF(const std::string &glyphURL,
+ const std::string &fontStack,
+ GlyphRange glyphRange,
+ Environment &env)
+ : future(promise.get_future().share()) {
// Load the glyph set URL
std::string url = util::replaceTokens(glyphURL, [&](const std::string &name) -> std::string {
if (name == "fontstack") return util::percentEncode(fontStack);
@@ -148,7 +151,7 @@ GlyphPBF::GlyphPBF(const std::string &glyphURL, const std::string &fontStack, Gl
});
// The prepare call jumps back to the main thread.
- fileSource.request({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) {
+ env.requestAsync({ Resource::Kind::Glyphs, url }, [&, url](const Response &res) {
if (res.status != Response::Successful) {
// Something went wrong with loading the glyph pbf. Pass on the error to the future listeners.
const std::string msg = std::string { "[ERROR] failed to load glyphs: " } + res.message;
@@ -223,7 +226,7 @@ void GlyphPBF::parse(FontStack &stack) {
data.clear();
}
-GlyphStore::GlyphStore(FileSource& fileSource_) : fileSource(fileSource_), mtx(util::make_unique<uv::mutex>()) {}
+GlyphStore::GlyphStore(Environment& env_) : env(env_), mtx(util::make_unique<uv::mutex>()) {}
void GlyphStore::setURL(const std::string &url) {
glyphURL = url;
@@ -265,7 +268,7 @@ std::shared_future<GlyphPBF &> GlyphStore::loadGlyphRange(const std::string &fon
auto range_it = rangeSets.find(range);
if (range_it == rangeSets.end()) {
// We don't have this glyph set yet for this font stack.
- range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, fileSource)).first;
+ range_it = rangeSets.emplace(range, util::make_unique<GlyphPBF>(glyphURL, fontStack, range, env)).first;
}
return range_it->second->getFuture();
diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp
index 6839045d61..406234241d 100644
--- a/src/mbgl/text/glyph_store.hpp
+++ b/src/mbgl/text/glyph_store.hpp
@@ -17,6 +17,7 @@
namespace mbgl {
class FileSource;
+class Environment;
class SDFGlyph {
public:
@@ -49,7 +50,10 @@ private:
class GlyphPBF {
public:
- GlyphPBF(const std::string &glyphURL, const std::string &fontStack, GlyphRange glyphRange, FileSource& fileSource);
+ GlyphPBF(const std::string &glyphURL,
+ const std::string &fontStack,
+ GlyphRange glyphRange,
+ Environment &env);
private:
GlyphPBF(const GlyphPBF &) = delete;
@@ -72,7 +76,7 @@ private:
// Manages Glyphrange PBF loading.
class GlyphStore {
public:
- GlyphStore(FileSource& fileSource);
+ GlyphStore(Environment &);
// Block until all specified GlyphRanges of the specified font stack are loaded.
void waitForGlyphRanges(const std::string &fontStack, const std::set<GlyphRange> &glyphRanges);
@@ -88,7 +92,7 @@ private:
FontStack &createFontStack(const std::string &fontStack);
std::string glyphURL;
- FileSource& fileSource;
+ Environment &env;
std::unordered_map<std::string, std::map<GlyphRange, std::unique_ptr<GlyphPBF>>> ranges;
std::unordered_map<std::string, std::unique_ptr<FontStack>> stacks;
std::unique_ptr<uv::mutex> mtx;
diff --git a/test/storage/cache_response.cpp b/test/storage/cache_response.cpp
index a0b5ba31c1..ac0dc4c565 100644
--- a/test/storage/cache_response.cpp
+++ b/test/storage/cache_response.cpp
@@ -14,8 +14,9 @@ TEST_F(Storage, CacheResponse) {
DefaultFileSource fs(&cache, uv_default_loop());
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/cache" };
+ auto &env = *static_cast<const Environment *>(nullptr);
- fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_LT(0, res.expires);
@@ -23,7 +24,7 @@ TEST_F(Storage, CacheResponse) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(resource, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(resource, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(res.status, res2.status);
EXPECT_EQ(res.data, res2.data);
EXPECT_EQ(res.expires, res2.expires);
diff --git a/test/storage/cache_revalidate.cpp b/test/storage/cache_revalidate.cpp
index 530b7325b5..bd32042b94 100644
--- a/test/storage/cache_revalidate.cpp
+++ b/test/storage/cache_revalidate.cpp
@@ -15,8 +15,10 @@ TEST_F(Storage, CacheRevalidate) {
SQLiteCache cache(":memory:");
DefaultFileSource fs(&cache);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
- fs.request(revalidateSame, uv_default_loop(), [&](const Response &res) {
+ fs.request(revalidateSame, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -24,7 +26,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("snowfall", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateSame, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateSame, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response", res2.data);
// We use this to indicate that a 304 reply came back.
@@ -38,8 +40,9 @@ TEST_F(Storage, CacheRevalidate) {
});
});
- const Resource revalidateModified { Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" };
- fs.request(revalidateModified, uv_default_loop(), [&](const Response &res) {
+ const Resource revalidateModified{ Resource::Unknown,
+ "http://127.0.0.1:3000/revalidate-modified" };
+ fs.request(revalidateModified, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response", res.data);
EXPECT_EQ(0, res.expires);
@@ -47,7 +50,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateModified, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateModified, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response", res2.data);
// We use this to indicate that a 304 reply came back.
@@ -61,7 +64,7 @@ TEST_F(Storage, CacheRevalidate) {
});
const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
- fs.request(revalidateEtag, uv_default_loop(), [&](const Response &res) {
+ fs.request(revalidateEtag, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Response 1", res.data);
EXPECT_EQ(0, res.expires);
@@ -69,7 +72,7 @@ TEST_F(Storage, CacheRevalidate) {
EXPECT_EQ("response-1", res.etag);
EXPECT_EQ("", res.message);
- fs.request(revalidateEtag, uv_default_loop(), [&, res](const Response &res2) {
+ fs.request(revalidateEtag, uv_default_loop(), env, [&, res](const Response &res2) {
EXPECT_EQ(Response::Successful, res2.status);
EXPECT_EQ("Response 2", res2.data);
EXPECT_EQ(0, res2.expires);
diff --git a/test/storage/directory_reading.cpp b/test/storage/directory_reading.cpp
index 3ee4dd1721..a955648462 100644
--- a/test/storage/directory_reading.cpp
+++ b/test/storage/directory_reading.cpp
@@ -15,7 +15,10 @@ TEST_F(Storage, AssetReadDirectory) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
- fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -24,7 +27,7 @@ TEST_F(Storage, AssetReadDirectory) {
#ifdef MBGL_ASSET_ZIP
EXPECT_EQ("No such file", res.message);
#elif MBGL_ASSET_FS
- EXPECT_EQ("illegal operation on a directory", res.message);
+ EXPECT_EQ("illegal operation on a directory", res.message);
#endif
ReadDirectory.finish();
});
diff --git a/test/storage/file_reading.cpp b/test/storage/file_reading.cpp
index 7e14fdcb15..cca072b27a 100644
--- a/test/storage/file_reading.cpp
+++ b/test/storage/file_reading.cpp
@@ -16,9 +16,10 @@ TEST_F(Storage, AssetEmptyFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
- fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" },
- uv_default_loop(),
- [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/empty" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -42,9 +43,10 @@ TEST_F(Storage, AssetNonEmptyFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
+ auto &env = *static_cast<const Environment *>(nullptr);
+
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/nonempty" },
- uv_default_loop(),
- [&](const Response &res) {
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(16ul, res.data.size());
EXPECT_EQ(0, res.expires);
@@ -69,9 +71,10 @@ TEST_F(Storage, AssetNonExistentFile) {
DefaultFileSource fs(nullptr, uv_default_loop());
#endif
+ auto &env = *static_cast<const Environment *>(nullptr);
+
fs.request({ Resource::Unknown, "asset://TEST_DATA/fixtures/storage/does_not_exist" },
- uv_default_loop(),
- [&](const Response &res) {
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ(0ul, res.data.size());
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_cancel.cpp b/test/storage/http_cancel.cpp
index 7e485ec42d..d0dac8ccdb 100644
--- a/test/storage/http_cancel.cpp
+++ b/test/storage/http_cancel.cpp
@@ -14,9 +14,11 @@ TEST_F(Storage, HTTPCancel) {
DefaultFileSource fs(nullptr, uv_default_loop());
- auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &) {
- ADD_FAILURE() << "Callback should not be called";
- });
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ auto req =
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &) { ADD_FAILURE() << "Callback should not be called"; });
fs.cancel(req);
HTTPCancel.finish();
@@ -31,11 +33,13 @@ TEST_F(Storage, HTTPCancelMultiple) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
- auto req2 = fs.request(resource, uv_default_loop(), [&](const Response &) {
+
+ auto req2 = fs.request(resource, uv_default_loop(), env, [&](const Response &) {
ADD_FAILURE() << "Callback should not be called";
});
- fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ fs.request(resource, uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_coalescing.cpp b/test/storage/http_coalescing.cpp
index 592c65f8d6..9eaea70e11 100644
--- a/test/storage/http_coalescing.cpp
+++ b/test/storage/http_coalescing.cpp
@@ -14,6 +14,8 @@ TEST_F(Storage, HTTPCoalescing) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
static const Response *reference = nullptr;
const auto complete = [&](const Response &res) {
@@ -41,7 +43,7 @@ TEST_F(Storage, HTTPCoalescing) {
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
for (int i = 0; i < total; i++) {
- fs.request(resource, uv_default_loop(), complete);
+ fs.request(resource, uv_default_loop(), env, complete);
}
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
diff --git a/test/storage/http_environment.cpp b/test/storage/http_environment.cpp
new file mode 100644
index 0000000000..efd7b78ad4
--- /dev/null
+++ b/test/storage/http_environment.cpp
@@ -0,0 +1,54 @@
+#include "storage.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default_file_source.hpp>
+#include <mbgl/storage/network_status.hpp>
+
+#include <cmath>
+
+TEST_F(Storage, HTTPCancelEnvironment) {
+ SCOPED_TEST(HTTPRetainedEnvironment)
+ SCOPED_TEST(HTTPCanceledEnvironment)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ // Create two fake environment pointers. The FileSource implementation treats these as opaque
+ // pointers and doesn't reach into them.
+ auto &env1 = *reinterpret_cast<const Environment *>(1);
+ auto &env2 = *reinterpret_cast<const Environment *>(2);
+
+ const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/delayed" };
+
+ // Environment 1
+ fs.request(resource, uv_default_loop(), env1, [&](const Response &res) {
+ // This environment gets aborted below. This means the request is marked as failing and
+ // will return an error here.
+ EXPECT_EQ(Response::Error, res.status);
+ EXPECT_EQ("", res.data);
+ EXPECT_EQ(0, res.expires);
+ EXPECT_EQ(0, res.modified);
+ EXPECT_EQ("", res.etag);
+ EXPECT_EQ("Environment is terminating", res.message);
+ HTTPCanceledEnvironment.finish();
+ });
+
+ // Environment 2
+ fs.request(resource, uv_default_loop(), env2, [&](const Response &res) {
+ // The same request as above, but in a different environment which doesn't get aborted. This
+ // means the request should succeed.
+ EXPECT_EQ(Response::Successful, res.status);
+ EXPECT_EQ("Response", res.data);
+ EXPECT_EQ(0, res.expires);
+ EXPECT_EQ(0, res.modified);
+ EXPECT_EQ("", res.etag);
+ EXPECT_EQ("", res.message);
+ HTTPRetainedEnvironment.finish();
+ });
+
+ fs.abort(env1);
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_error.cpp b/test/storage/http_error.cpp
index 498f1eae6b..abaeff5396 100644
--- a/test/storage/http_error.cpp
+++ b/test/storage/http_error.cpp
@@ -22,9 +22,12 @@ TEST_F(Storage, HTTPError) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
auto start = uv_hrtime();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(),
+ env, [&](const Response &res) {
const auto duration = double(uv_hrtime() - start) / 1e9;
EXPECT_LT(1, duration) << "Backoff timer didn't wait 1 second";
EXPECT_GT(1.2, duration) << "Backoff timer fired too late";
@@ -38,13 +41,21 @@ TEST_F(Storage, HTTPError) {
HTTPTemporaryError.finish();
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), env,
+ [&](const Response &res) {
const auto duration = double(uv_hrtime() - start) / 1e9;
// 1.5 seconds == 4 retries, with a 500ms timeout (see above).
EXPECT_LT(1.5, duration) << "Resource wasn't retried the correct number of times";
EXPECT_GT(1.7, duration) << "Resource wasn't retried the correct number of times";
EXPECT_EQ(Response::Error, res.status);
- EXPECT_TRUE(res.message == "Couldn't connect to server: couldn't connect to host" || res.message == "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)") << res.message;
+#ifdef MBGL_HTTP_NSURL
+ EXPECT_STREQ(res.message.c_str(), "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)");
+#elif MBGL_HTTP_CURL
+ const std::string prefix { "Couldn't connect to server: " };
+ EXPECT_STREQ(prefix.c_str(), res.message.substr(0, prefix.size()).c_str()) << "Full message is: \"" << res.message << "\"";
+#else
+ FAIL();
+#endif
EXPECT_EQ("", res.data);
EXPECT_EQ(0, res.expires);
EXPECT_EQ(0, res.modified);
@@ -52,7 +63,6 @@ TEST_F(Storage, HTTPError) {
HTTPConnectionError.finish();
});
-
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
uv_timer_stop(&statusChange);
diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp
index 655348c877..271460387c 100644
--- a/test/storage/http_header_parsing.cpp
+++ b/test/storage/http_header_parsing.cpp
@@ -14,7 +14,11 @@ TEST_F(Storage, HTTPHeaderParsing) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown,
+ "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" },
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(1420797926, res.expires);
@@ -24,11 +28,11 @@ TEST_F(Storage, HTTPHeaderParsing) {
HTTPExpiresTest.finish();
});
-
int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" },
+ uv_default_loop(), env, [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_GT(2, std::abs(res.expires - now - 120)) << "Expiration date isn't about 120 seconds in the future";
diff --git a/test/storage/http_load.cpp b/test/storage/http_load.cpp
index fff662da29..2680daf93b 100644
--- a/test/storage/http_load.cpp
+++ b/test/storage/http_load.cpp
@@ -11,13 +11,17 @@ TEST_F(Storage, HTTPLoad) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const int concurrency = 50;
const int max = 10000;
int number = 1;
std::function<void()> req = [&]() {
const auto current = number++;
- fs.request({ Resource::Unknown, std::string("http://127.0.0.1:3000/load/") + std::to_string(current) }, uv_default_loop(), [&, current](const Response &res) {
+ fs.request({ Resource::Unknown,
+ std::string("http://127.0.0.1:3000/load/") + std::to_string(current) },
+ uv_default_loop(), env, [&, current](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ(std::string("Request ") + std::to_string(current), res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_noloop.cpp b/test/storage/http_noloop.cpp
index c71fd0bc26..cd4ebeb2c4 100644
--- a/test/storage/http_noloop.cpp
+++ b/test/storage/http_noloop.cpp
@@ -12,6 +12,8 @@ TEST_F(Storage, HTTPNoLoop) {
DefaultFileSource fs(nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const auto mainThread = uv_thread_self();
// Async handle that keeps the main loop alive until the thread finished
@@ -20,7 +22,8 @@ TEST_F(Storage, HTTPNoLoop) {
uv::close(as);
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, nullptr, env,
+ [&](const Response &res) {
EXPECT_NE(uv_thread_self(), mainThread) << "Response was called in the same thread";
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp
index 06cc6f7476..9ad673cf79 100644
--- a/test/storage/http_other_loop.cpp
+++ b/test/storage/http_other_loop.cpp
@@ -12,7 +12,10 @@ TEST_F(Storage, HTTPOtherLoop) {
// This file source launches a separate thread to do the processing.
DefaultFileSource fs(nullptr);
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &res) {
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
EXPECT_EQ(0, res.expires);
diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp
index ad87db15ff..a6a5775825 100644
--- a/test/storage/http_reading.cpp
+++ b/test/storage/http_reading.cpp
@@ -12,9 +12,12 @@ TEST_F(Storage, HTTPReading) {
DefaultFileSource fs(nullptr, uv_default_loop());
+ auto &env = *static_cast<const Environment *>(nullptr);
+
const auto mainThread = uv_thread_self();
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ [&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Successful, res.status);
EXPECT_EQ("Hello World!", res.data);
@@ -25,7 +28,8 @@ TEST_F(Storage, HTTPReading) {
HTTPTest.finish();
});
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(), [&](const Response &res) {
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(),
+ env, [&](const Response &res) {
EXPECT_EQ(uv_thread_self(), mainThread);
EXPECT_EQ(Response::Error, res.status);
EXPECT_EQ("HTTP status code 404", res.message);
@@ -45,7 +49,10 @@ TEST_F(Storage, HTTPNoCallback) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), env,
+ nullptr);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
@@ -59,7 +66,9 @@ TEST_F(Storage, HTTPNoCallbackNoLoop) {
DefaultFileSource fs(nullptr, uv_default_loop());
- fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, nullptr);
+ auto &env = *static_cast<const Environment *>(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, env, nullptr);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
diff --git a/test/storage/server.js b/test/storage/server.js
index 33d5a82985..1ee5363196 100755
--- a/test/storage/server.js
+++ b/test/storage/server.js
@@ -85,6 +85,13 @@ app.get('/temporary-error', function(req, res) {
temporaryErrorCounter++;
});
+app.get('/delayed', function(req, res) {
+ setTimeout(function() {
+ res.status(200).send('Response');
+ }, 200);
+});
+
+
app.get('/load/:number(\\d+)', function(req, res) {
res.send('Request ' + req.params.number);
});
diff --git a/test/test.gyp b/test/test.gyp
index 1d80a4f830..7759caced8 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -56,6 +56,7 @@
'storage/file_reading.cpp',
'storage/http_cancel.cpp',
'storage/http_coalescing.cpp',
+ 'storage/http_environment.cpp',
'storage/http_error.cpp',
'storage/http_header_parsing.cpp',
'storage/http_load.cpp',