summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/map/environment.hpp3
-rw-r--r--include/mbgl/map/map.hpp12
-rw-r--r--src/mbgl/map/environment.cpp5
-rw-r--r--src/mbgl/map/map.cpp174
-rw-r--r--src/mbgl/map/map_context.cpp271
-rw-r--r--src/mbgl/map/map_context.hpp71
-rw-r--r--src/mbgl/map/map_data.hpp19
-rw-r--r--src/mbgl/util/run_loop.hpp4
-rw-r--r--src/mbgl/util/signal.cpp21
-rw-r--r--src/mbgl/util/signal.hpp24
-rw-r--r--src/mbgl/util/thread.hpp51
11 files changed, 198 insertions, 457 deletions
diff --git a/include/mbgl/map/environment.hpp b/include/mbgl/map/environment.hpp
index a00fb24454..07554a7a92 100644
--- a/include/mbgl/map/environment.hpp
+++ b/include/mbgl/map/environment.hpp
@@ -62,9 +62,6 @@ private:
std::vector<uint32_t> abandonedVAOs;
std::vector<uint32_t> abandonedBuffers;
std::vector<uint32_t> abandonedTextures;
-
-public:
- uv_loop_t* const loop;
};
class EnvironmentScope final {
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp
index 07e6b65e16..b217a685f9 100644
--- a/include/mbgl/map/map.hpp
+++ b/include/mbgl/map/map.hpp
@@ -27,6 +27,10 @@ class MapData;
class MapContext;
class StillImage;
+namespace util {
+template <class T> class Thread;
+}
+
class Map : private util::noncopyable {
friend class View;
@@ -55,6 +59,7 @@ public:
// Triggers a synchronous or asynchronous render.
void renderSync();
+ void renderAsync();
// Notifies the Map thread that the state has changed and an update might be necessary.
void update();
@@ -139,10 +144,7 @@ public:
bool getDebug() const;
private:
- // Runs the map event loop. ONLY run this function when you want to get render a single frame
- // with this map object. It will *not* spawn a separate thread and instead block until the
- // frame is completely rendered.
- void run();
+ void triggerUpdate(Update update = Update::Nothing);
// This may only be called by the View object.
void resize(uint16_t width, uint16_t height, float ratio = 1);
@@ -152,7 +154,7 @@ private:
std::unique_ptr<EnvironmentScope> scope;
View &view;
const std::unique_ptr<MapData> data;
- std::unique_ptr<MapContext> context;
+ std::unique_ptr<util::Thread<MapContext>> context;
};
}
diff --git a/src/mbgl/map/environment.cpp b/src/mbgl/map/environment.cpp
index 4c7a5faf81..dd6c1f1933 100644
--- a/src/mbgl/map/environment.cpp
+++ b/src/mbgl/map/environment.cpp
@@ -1,6 +1,7 @@
#include <mbgl/map/environment.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/run_loop.hpp>
#include <uv.h>
@@ -88,7 +89,7 @@ EnvironmentScope::~EnvironmentScope() {
}
Environment::Environment(FileSource& fs)
- : id(makeEnvironmentID()), fileSource(fs), loop(uv_loop_new()) {
+ : id(makeEnvironmentID()), fileSource(fs) {
}
Environment::~Environment() {
@@ -128,7 +129,7 @@ void Environment::requestAsync(const Resource& resource,
Request* Environment::request(const Resource& resource,
std::function<void(const Response&)> callback) {
assert(currentlyOn(ThreadType::Map));
- return fileSource.request(resource, loop, std::move(callback));
+ return fileSource.request(resource, util::RunLoop::current.get()->get(), std::move(callback));
}
void Environment::cancelRequest(Request* req) {
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index 46381cc903..1d05c073de 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -23,6 +23,7 @@
#include <mbgl/util/mapbox.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/worker.hpp>
+#include <mbgl/util/thread.hpp>
#include <algorithm>
#include <iostream>
@@ -36,8 +37,7 @@ Map::Map(View& view_, FileSource& fileSource_)
: env(util::make_unique<Environment>(fileSource_)),
scope(util::make_unique<EnvironmentScope>(*env, ThreadType::Main, "Main")),
view(view_),
- data(util::make_unique<MapData>(view_)),
- context(util::make_unique<MapContext>(*env, view, *data))
+ data(util::make_unique<MapData>(view_))
{
view.initialize(this);
}
@@ -46,19 +46,6 @@ Map::~Map() {
if (data->mode != MapMode::None) {
stop();
}
-
- // Extend the scope to include both Main and Map thread types to ease cleanup.
- scope.reset();
- scope = util::make_unique<EnvironmentScope>(
- *env, static_cast<ThreadType>(static_cast<uint8_t>(ThreadType::Main) |
- static_cast<uint8_t>(ThreadType::Map)),
- "MapandMain");
-
- context.reset();
-
- uv_run(env->loop, UV_RUN_DEFAULT);
-
- env->performCleanup();
}
void Map::start(bool startPaused, MapMode renderMode) {
@@ -69,24 +56,14 @@ void Map::start(bool startPaused, MapMode renderMode) {
// updated rendering. Only in these cases, we attach the async handlers.
data->mode = renderMode;
- // Reset the flag.
- data->isStopped = false;
-
- // Do we need to pause first?
- if (startPaused) {
- pause();
- }
-
- context->start();
- context->triggerUpdate();
+ context = util::make_unique<util::Thread<MapContext>>("Map", *env, view, *data, startPaused);
+ triggerUpdate();
}
void Map::stop(std::function<void ()> cb) {
assert(Environment::currentlyOn(ThreadType::Main));
assert(data->mode != MapMode::None);
- context->terminate();
-
resume();
if (cb) {
@@ -96,14 +73,12 @@ void Map::stop(std::function<void ()> cb) {
// the case with Cocoa's NSURLRequest. Otherwise, we will eventually deadlock because this
// thread (== main thread) is blocked. The callback function should use an efficient waiting
// function to avoid a busy waiting loop.
- while (!data->isStopped) {
- cb();
- }
+ context->pumpingStop(cb);
}
// If a callback function was provided, this should return immediately because the thread has
// already finished executing.
- data->thread.join();
+ context.reset();
data->mode = MapMode::None;
}
@@ -111,33 +86,27 @@ void Map::stop(std::function<void ()> cb) {
void Map::pause(bool waitForPause) {
assert(Environment::currentlyOn(ThreadType::Main));
assert(data->mode == MapMode::Continuous);
- data->mutexRun.lock();
- data->pausing = true;
- data->mutexRun.unlock();
+ assert(context);
- uv_stop(env->loop);
- context->triggerUpdate(); // Needed to ensure uv_stop is seen and uv_run exits, otherwise we deadlock on wait_for_pause
+ std::unique_lock<std::mutex> lockPause(data->mutexPause);
+ context->invoke(&MapContext::pause);
if (waitForPause) {
- std::unique_lock<std::mutex> lockPause (data->mutexPause);
- while (!data->isPaused) {
- data->condPause.wait(lockPause);
- }
+ data->condPaused.wait(lockPause);
}
}
void Map::resume() {
assert(Environment::currentlyOn(ThreadType::Main));
assert(data->mode != MapMode::None);
+ assert(context);
- data->mutexRun.lock();
- data->pausing = false;
- data->condRun.notify_all();
- data->mutexRun.unlock();
+ data->condResume.notify_all();
}
void Map::renderStill(StillImageCallback fn) {
assert(Environment::currentlyOn(ThreadType::Main));
+ assert(context);
if (data->mode != MapMode::Still) {
throw util::Exception("Map is not in still image render mode");
@@ -147,24 +116,27 @@ void Map::renderStill(StillImageCallback fn) {
throw util::Exception("Map is currently rendering an image");
}
- assert(data->mode == MapMode::Still);
-
data->callback = std::move(fn);
-
- context->triggerUpdate(Update::RenderStill);
+ triggerUpdate(Update::RenderStill);
}
void Map::renderSync() {
- // Must be called in UI thread.
assert(Environment::currentlyOn(ThreadType::Main));
+ assert(context);
+
+ context->invokeSync(&MapContext::render);
+}
- context->triggerRender();
+void Map::renderAsync() {
+ assert(Environment::currentlyOn(ThreadType::Main));
+ assert(context);
- data->rendered.wait();
+ context->invoke(&MapContext::render);
}
void Map::update() {
- context->triggerUpdate();
+ assert(context);
+ triggerUpdate();
}
#pragma mark - Setup
@@ -185,14 +157,14 @@ void Map::setStyleURL(const std::string &url) {
}
data->setStyleInfo({ styleURL, base, "" });
- context->triggerUpdate(Update::StyleInfo);
+ triggerUpdate(Update::StyleInfo);
}
void Map::setStyleJSON(const std::string& json, const std::string& base) {
assert(Environment::currentlyOn(ThreadType::Main));
data->setStyleInfo({ "", base, json });
- context->triggerUpdate(Update::StyleInfo);
+ triggerUpdate(Update::StyleInfo);
}
std::string Map::getStyleJSON() const {
@@ -207,7 +179,7 @@ void Map::resize(uint16_t width, uint16_t height, float ratio) {
void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth, uint16_t fbHeight) {
if (data->transform.resize(width, height, ratio, fbWidth, fbHeight)) {
- context->triggerUpdate();
+ triggerUpdate();
}
}
@@ -215,24 +187,24 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fbWidth,
void Map::cancelTransitions() {
data->transform.cancelTransitions();
- context->triggerUpdate();
+ triggerUpdate();
}
void Map::setGestureInProgress(bool inProgress) {
data->transform.setGestureInProgress(inProgress);
- context->triggerUpdate();
+ triggerUpdate();
}
#pragma mark - Position
void Map::moveBy(double dx, double dy, Duration duration) {
data->transform.moveBy(dx, dy, duration);
- context->triggerUpdate();
+ triggerUpdate();
}
void Map::setLatLng(LatLng latLng, Duration duration) {
data->transform.setLatLng(latLng, duration);
- context->triggerUpdate();
+ triggerUpdate();
}
LatLng Map::getLatLng() const {
@@ -243,7 +215,7 @@ void Map::resetPosition() {
data->transform.setAngle(0);
data->transform.setLatLng(LatLng(0, 0));
data->transform.setZoom(0);
- context->triggerUpdate(Update::Zoom);
+ triggerUpdate(Update::Zoom);
}
@@ -251,12 +223,12 @@ void Map::resetPosition() {
void Map::scaleBy(double ds, double cx, double cy, Duration duration) {
data->transform.scaleBy(ds, cx, cy, duration);
- context->triggerUpdate(Update::Zoom);
+ triggerUpdate(Update::Zoom);
}
void Map::setScale(double scale, double cx, double cy, Duration duration) {
data->transform.setScale(scale, cx, cy, duration);
- context->triggerUpdate(Update::Zoom);
+ triggerUpdate(Update::Zoom);
}
double Map::getScale() const {
@@ -265,7 +237,7 @@ double Map::getScale() const {
void Map::setZoom(double zoom, Duration duration) {
data->transform.setZoom(zoom, duration);
- context->triggerUpdate(Update::Zoom);
+ triggerUpdate(Update::Zoom);
}
double Map::getZoom() const {
@@ -274,7 +246,7 @@ double Map::getZoom() const {
void Map::setLatLngZoom(LatLng latLng, double zoom, Duration duration) {
data->transform.setLatLngZoom(latLng, zoom, duration);
- context->triggerUpdate(Update::Zoom);
+ triggerUpdate(Update::Zoom);
}
void Map::resetZoom() {
@@ -305,17 +277,17 @@ uint16_t Map::getHeight() const {
void Map::rotateBy(double sx, double sy, double ex, double ey, Duration duration) {
data->transform.rotateBy(sx, sy, ex, ey, duration);
- context->triggerUpdate();
+ triggerUpdate();
}
void Map::setBearing(double degrees, Duration duration) {
data->transform.setAngle(-degrees * M_PI / 180, duration);
- context->triggerUpdate();
+ triggerUpdate();
}
void Map::setBearing(double degrees, double cx, double cy) {
data->transform.setAngle(-degrees * M_PI / 180, cx, cy);
- context->triggerUpdate();
+ triggerUpdate();
}
double Map::getBearing() const {
@@ -324,7 +296,7 @@ double Map::getBearing() const {
void Map::resetNorth() {
data->transform.setAngle(0, std::chrono::milliseconds(500));
- context->triggerUpdate();
+ triggerUpdate();
}
@@ -373,16 +345,13 @@ const LatLng Map::latLngForPixel(const vec2<double> pixel) const {
void Map::setDefaultPointAnnotationSymbol(const std::string& symbol) {
assert(Environment::currentlyOn(ThreadType::Main));
- context->invokeTask([=] {
- context->setDefaultPointAnnotationSymbol(symbol);
- });
+ data->annotationManager.setDefaultPointAnnotationSymbol(symbol);
}
double Map::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
assert(Environment::currentlyOn(ThreadType::Main));
- return context->invokeSyncTask([&] {
- return context->getTopOffsetPixelsForAnnotationSymbol(symbol);
- });
+ assert(context);
+ return context->invokeSync<double>(&MapContext::getTopOffsetPixelsForAnnotationSymbol, symbol);
}
uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) {
@@ -391,9 +360,11 @@ uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol)
std::vector<uint32_t> Map::addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols) {
assert(Environment::currentlyOn(ThreadType::Main));
- return context->invokeSyncTask([&] {
- return context->addPointAnnotations(points, symbols);
- });
+ auto result = data->annotationManager.addPointAnnotations(points, symbols, *data);
+ if (context) {
+ context->invoke(&MapContext::updateAnnotationTiles, result.first);
+ }
+ return result.second;
}
void Map::removeAnnotation(uint32_t annotation) {
@@ -403,23 +374,20 @@ void Map::removeAnnotation(uint32_t annotation) {
void Map::removeAnnotations(const std::vector<uint32_t>& annotations) {
assert(Environment::currentlyOn(ThreadType::Main));
- context->invokeTask([=] {
- context->removeAnnotations(annotations);
- });
+ auto result = data->annotationManager.removeAnnotations(annotations, *data);
+ if (context) {
+ context->invoke(&MapContext::updateAnnotationTiles, result);
+ }
}
std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds) {
assert(Environment::currentlyOn(ThreadType::Main));
- return context->invokeSyncTask([&] {
- return context->getAnnotationsInBounds(bounds);
- });
+ return data->annotationManager.getAnnotationsInBounds(bounds, *data);
}
LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) {
assert(Environment::currentlyOn(ThreadType::Main));
- return context->invokeSyncTask([&] {
- return context->getBoundsForAnnotations(annotations);
- });
+ return data->annotationManager.getBoundsForAnnotations(annotations);
}
@@ -427,12 +395,12 @@ LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotatio
void Map::setDebug(bool value) {
data->setDebug(value);
- context->triggerUpdate(Update::Debug);
+ triggerUpdate(Update::Debug);
}
void Map::toggleDebug() {
data->toggleDebug();
- context->triggerUpdate(Update::Debug);
+ triggerUpdate(Update::Debug);
}
bool Map::getDebug() const {
@@ -441,19 +409,19 @@ bool Map::getDebug() const {
void Map::addClass(const std::string& klass) {
if (data->addClass(klass)) {
- context->triggerUpdate(Update::Classes);
+ triggerUpdate(Update::Classes);
}
}
void Map::removeClass(const std::string& klass) {
if (data->removeClass(klass)) {
- context->triggerUpdate(Update::Classes);
+ triggerUpdate(Update::Classes);
}
}
void Map::setClasses(const std::vector<std::string>& classes) {
data->setClasses(classes);
- context->triggerUpdate(Update::Classes);
+ triggerUpdate(Update::Classes);
}
bool Map::hasClass(const std::string& klass) const {
@@ -468,7 +436,7 @@ void Map::setDefaultTransitionDuration(Duration duration) {
assert(Environment::currentlyOn(ThreadType::Main));
data->setDefaultTransitionDuration(duration);
- context->triggerUpdate(Update::DefaultTransitionDuration);
+ triggerUpdate(Update::DefaultTransitionDuration);
}
Duration Map::getDefaultTransitionDuration() {
@@ -476,19 +444,23 @@ Duration Map::getDefaultTransitionDuration() {
return data->getDefaultTransitionDuration();
}
-#pragma mark - Private
-
-
void Map::setSourceTileCacheSize(size_t size) {
- context->invokeTask([=] {
- context->setSourceTileCacheSize(size);
- });
+ assert(context);
+ context->invoke(&MapContext::setSourceTileCacheSize, size);
}
void Map::onLowMemory() {
- context->invokeTask([=] {
- context->onLowMemory();
- });
+ if (context) {
+ context->invoke(&MapContext::onLowMemory);
+ }
+}
+
+#pragma mark - Private
+
+void Map::triggerUpdate(Update update_) {
+ if (context) {
+ context->invoke(&MapContext::triggerUpdate, update_);
+ }
}
}
diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp
index 525425205e..1a5543447b 100644
--- a/src/mbgl/map/map_context.cpp
+++ b/src/mbgl/map/map_context.cpp
@@ -29,198 +29,66 @@
namespace mbgl {
-MapContext::MapContext(Environment& env_, View& view_, MapData& data_)
+MapContext::MapContext(uv_loop_t* loop, Environment& env_, View& view_, MapData& data_, bool startPaused)
: env(env_),
view(view_),
data(data_),
+ mapScope(env, ThreadType::Map, "Map"),
updated(static_cast<UpdateType>(Update::Nothing)),
+ asyncUpdate(util::make_unique<uv::async>(loop, [this] { update(); })),
+ workers(util::make_unique<Worker>(loop, 4)),
glyphStore(util::make_unique<GlyphStore>(env)),
glyphAtlas(util::make_unique<GlyphAtlas>(1024, 1024)),
spriteAtlas(util::make_unique<SpriteAtlas>(512, 512)),
lineAtlas(util::make_unique<LineAtlas>(512, 512)),
texturePool(util::make_unique<TexturePool>()),
- painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas)) {
-}
-
-MapContext::~MapContext() {
- // TODO: does this need to happen first for some reason?
- style.reset();
-}
-
-void MapContext::start() {
- // Setup async notifications
- assert(!asyncTerminate);
- asyncTerminate = util::make_unique<uv::async>(env.loop, [this]() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
- // Remove all of these to make sure they are destructed in the correct thread.
- style.reset();
-
- // It's now safe to destroy/join the workers since there won't be any more callbacks that
- // could dispatch to the worker pool.
- workers.reset();
-
- data.terminating = true;
-
- // Closes all open handles on the loop. This means that the loop will automatically terminate.
- asyncRender.reset();
- asyncUpdate.reset();
- asyncInvoke.reset();
- asyncTerminate.reset();
- });
-
- assert(!asyncUpdate);
- asyncUpdate = util::make_unique<uv::async>(env.loop, [this] {
- // of the calls actually get triggered.
- asyncUpdate->unref();
-
- update();
- });
-
- assert(!asyncInvoke);
- asyncInvoke = util::make_unique<uv::async>(env.loop, [this] {
- processTasks();
- });
-
- assert(!asyncRender);
- asyncRender = util::make_unique<uv::async>(env.loop, [this] {
- // Must be called in Map thread.
- assert(Environment::currentlyOn(ThreadType::Map));
-
- render();
-
- // Finally, notify all listeners that we have finished rendering this frame.
- data.rendered.notify();
- });
-
- data.thread = std::thread([this]() {
-#ifdef __APPLE__
- pthread_setname_np("Map");
-#endif
-
- EnvironmentScope mapScope(env, ThreadType::Map, "Map");
- assert(Environment::currentlyOn(ThreadType::Map));
- assert(data.mode != MapMode::None);
-
- if (data.mode == MapMode::Continuous) {
- checkForPause();
- }
-
- view.activate();
- view.discard();
-
- workers = util::make_unique<Worker>(env.loop, 4);
-
- assert(painter);
- painter->setup();
-
- prepare();
+ painter(util::make_unique<Painter>(*spriteAtlas, *glyphAtlas, *lineAtlas))
+{
+ assert(Environment::currentlyOn(ThreadType::Map));
+ assert(data.mode != MapMode::None);
- if (data.mode == MapMode::Continuous) {
- data.terminating = false;
- while (!data.terminating) {
- uv_run(env.loop, UV_RUN_DEFAULT);
- checkForPause();
- }
- } else if (data.mode == MapMode::Still) {
- data.terminating = false;
- while (!data.terminating) {
- uv_run(env.loop, UV_RUN_DEFAULT);
-
- // After the loop terminated, these async handles may have been deleted if the terminate()
- // callback was fired. In this case, we are exiting the loop.
- if (asyncTerminate && asyncUpdate) {
- // Otherwise, loop termination means that we have acquired and parsed all resources
- // required for this map image and we can now proceed to rendering.
- render();
- auto image = view.readStillImage();
-
- // We are moving the callback out of the way and empty it in case the callback function
- // starts the next map image render.
- assert(data.callback);
- MapData::StillImageCallback cb;
- std::swap(cb, data.callback);
-
- // Now we can finally invoke the callback function with the map image we rendered.
- cb(std::move(image));
-
- // To prepare for the next event loop run, we have to make sure the async handles keep
- // the loop alive.
- asyncTerminate->ref();
- asyncUpdate->ref();
- asyncInvoke->ref();
- asyncRender->ref();
- }
- }
- } else {
- abort();
- }
+ asyncUpdate->unref();
- // Run the event loop once more to make sure our async delete handlers are called.
- uv_run(env.loop, UV_RUN_ONCE);
+ view.activate();
- view.deactivate();
+ if (startPaused) {
+ pause();
+ }
- // Make sure that the stop() function knows when to stop invoking the callback function.
- data.isStopped = true;
- view.notify();
- });
+ painter->setup();
}
-void MapContext::checkForPause() {
- std::unique_lock<std::mutex> lockRun (data.mutexRun);
- while (data.pausing) {
- view.deactivate();
-
- data.mutexPause.lock();
- data.isPaused = true;
- data.condPause.notify_all();
- data.mutexPause.unlock();
-
- data.condRun.wait(lockRun);
+MapContext::~MapContext() {
+ view.deactivate();
+ view.notify();
- view.activate();
- }
+ // Explicit resets currently necessary because these abandon resources that need to be
+ // cleaned up by env.performCleanup();
+ style.reset();
+ sprite.reset();
+ painter.reset();
+ texturePool.reset();
+ lineAtlas.reset();
+ spriteAtlas.reset();
+ glyphAtlas.reset();
+ glyphStore.reset();
- data.mutexPause.lock();
- data.isPaused = false;
- data.mutexPause.unlock();
+ env.performCleanup();
}
-void MapContext::terminate() {
- assert(asyncTerminate);
- asyncTerminate->send();
-}
+void MapContext::pause() {
+ view.deactivate();
+
+ std::unique_lock<std::mutex> lockPause(data.mutexPause);
+ data.condPaused.notify_all();
+ data.condResume.wait(lockPause);
-void MapContext::triggerRender() {
- assert(data.mode == MapMode::Continuous);
- assert(asyncRender);
- asyncRender->send();
+ view.activate();
}
void MapContext::triggerUpdate(const Update u) {
updated |= static_cast<UpdateType>(u);
-
- if (asyncUpdate) {
- asyncUpdate->ref();
- asyncUpdate->send();
- }
-}
-
-// Runs the function in the map thread.
-void MapContext::invokeTask(std::function<void()>&& fn) {
- {
- std::lock_guard<std::mutex> lock(mutexTask);
- tasks.emplace(::std::forward<std::function<void()>>(fn));
- }
-
- // TODO: Once we have aligned static and continuous rendering, this should always dispatch
- // to the async queue.
- if (asyncInvoke) {
- asyncInvoke->send();
- } else {
- processTasks();
- }
+ asyncUpdate->send();
}
Worker& MapContext::getWorker() {
@@ -311,15 +179,6 @@ void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) {
void MapContext::update() {
assert(Environment::currentlyOn(ThreadType::Map));
- if (data.getTransformState().hasSize()) {
- prepare();
- }
-}
-
-
-void MapContext::prepare() {
- assert(Environment::currentlyOn(ThreadType::Map));
-
const auto now = Clock::now();
data.setAnimationTime(now);
@@ -344,14 +203,6 @@ void MapContext::prepare() {
if (u & static_cast<UpdateType>(Update::RenderStill)) {
// Triggers a view resize.
view.discard();
-
- // Whenever we trigger an image render, we are unrefing all async handles so the loop will
- // eventually terminate. However, it'll stay alive as long as there are pending requests
- // (like work requests or HTTP requests).
- asyncTerminate->unref();
- asyncUpdate->unref();
- asyncInvoke->unref();
- asyncRender->unref();
}
if (style) {
@@ -374,9 +225,7 @@ void MapContext::prepare() {
spriteAtlas->setSprite(getSprite());
updateTiles();
- }
- if (data.mode == MapMode::Continuous) {
view.invalidate([this] { render(); });
}
}
@@ -394,31 +243,24 @@ void MapContext::render() {
painter->render(*style, data.getTransformState(), data.getAnimationTime());
- // Schedule another rerender when we definitely need a next frame.
- if (data.transform.needsTransition() || style->hasTransitions()) {
- triggerUpdate();
- }
-}
+ if (data.mode == MapMode::Still && data.callback /* && loaded() */) {
+ auto image = view.readStillImage();
+ // We are moving the callback out of the way and empty it in case the callback function
+ // starts the next map image render.
+ MapData::StillImageCallback cb;
+ std::swap(cb, data.callback);
-// Processes the functions that should be run in the map thread.
-void MapContext::processTasks() {
- std::queue<std::function<void()>> queue;
- {
- std::lock_guard<std::mutex> lock(mutexTask);
- queue.swap(tasks);
+ // Now we can finally invoke the callback function with the map image we rendered.
+ cb(std::move(image));
}
- while (!queue.empty()) {
- queue.front()();
- queue.pop();
+ // Schedule another rerender when we definitely need a next frame.
+ if (data.transform.needsTransition() || style->hasTransitions()) {
+ triggerUpdate();
}
}
-void MapContext::setDefaultPointAnnotationSymbol(const std::string& symbol) {
- data.annotationManager.setDefaultPointAnnotationSymbol(symbol);
-}
-
double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
assert(Environment::currentlyOn(ThreadType::Map));
assert(sprite);
@@ -426,25 +268,6 @@ double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symb
return -pos.height / pos.pixelRatio / 2;
}
-std::vector<uint32_t> MapContext::addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols) {
- auto result = data.annotationManager.addPointAnnotations(points, symbols, data);
- updateAnnotationTiles(result.first);
- return result.second;
-}
-
-void MapContext::removeAnnotations(const std::vector<uint32_t>& annotations) {
- auto result = data.annotationManager.removeAnnotations(annotations, data);
- updateAnnotationTiles(result);
-}
-
-std::vector<uint32_t> MapContext::getAnnotationsInBounds(const LatLngBounds& bounds) {
- return data.annotationManager.getAnnotationsInBounds(bounds, data);
-}
-
-LatLngBounds MapContext::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) {
- return data.annotationManager.getBoundsForAnnotations(annotations);
-}
-
void MapContext::setSourceTileCacheSize(size_t size) {
assert(Environment::currentlyOn(ThreadType::Map));
if (size != sourceCacheSize) {
diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp
index 7a4e9123ee..4808010637 100644
--- a/src/mbgl/map/map_context.hpp
+++ b/src/mbgl/map/map_context.hpp
@@ -3,6 +3,7 @@
#include <mbgl/map/tile_id.hpp>
#include <mbgl/map/update.hpp>
+#include <mbgl/map/environment.hpp>
#include <mbgl/util/ptr.hpp>
#include <vector>
@@ -10,6 +11,8 @@
#include <future>
#include <thread>
+typedef struct uv_loop_s uv_loop_t;
+
namespace uv {
class async;
}
@@ -33,65 +36,29 @@ struct LatLngBounds;
class MapContext {
public:
- MapContext(Environment&, View&, MapData&);
+ MapContext(uv_loop_t*, Environment&, View&, MapData&, bool startPaused);
~MapContext();
- // Starts the map thread.
- void start();
-
- // Terminates the map thread
- void terminate();
-
- // Triggers a render. Can be called from any thread.
- void triggerRender();
+ void pause();
+ void render();
// Notifies the Map thread that the state has changed and an update might be necessary.
void triggerUpdate(Update = Update::Nothing);
- // Executes the function on the thread of the MapContext.
- void invokeTask(std::function<void()>&& fn);
-
- // Executes the function on the thread of the MapContext and blocks until it
- // has been run.
- template <typename Fn> auto invokeSyncTask(const Fn& fn) -> decltype(fn()) {
- std::promise<decltype(fn())> promise;
- invokeTask([&fn, &promise] { promise.set_value(fn()); });
- return promise.get_future().get();
- }
-
- void setDefaultPointAnnotationSymbol(const std::string& symbol);
double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol);
- std::vector<uint32_t> addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols);
- void removeAnnotations(const std::vector<uint32_t>& annotations);
- std::vector<uint32_t> getAnnotationsInBounds(const LatLngBounds&);
- LatLngBounds getBoundsForAnnotations(const std::vector<uint32_t>&);
+ void updateAnnotationTiles(const std::vector<TileID>&);
void setSourceTileCacheSize(size_t size);
void onLowMemory();
- // These can only be called from the Map thread.
private:
- // Checks if render thread needs to pause
- void checkForPause();
- void updateAnnotationTiles(const std::vector<TileID>& ids);
-
Worker& getWorker();
util::ptr<Sprite> getSprite();
void updateTiles();
- // Triggered by triggerUpdate();
+ // Update the state indicated by the accumulated Update flags, then render.
void update();
- // Prepares a map render by updating the tiles we need for the current view, as well as updating
- // the stylesheet.
- void prepare();
-
- // Unconditionally performs a render with the current map state.
- void render();
-
- // Runs the enqueued tasks.
- void processTasks();
-
// Loads the style set in the data object. Called by Update::StyleInfo
void reloadStyle();
@@ -102,24 +69,18 @@ private:
View& view;
MapData& data;
- std::atomic<UpdateType> updated { static_cast<UpdateType>(Update::Nothing) };
-
- std::mutex mutexTask;
- std::queue<std::function<void()>> tasks;
-
+ EnvironmentScope mapScope;
+ std::atomic<UpdateType> updated { static_cast<UpdateType>(Update::Nothing) };
std::unique_ptr<uv::async> asyncUpdate;
- std::unique_ptr<uv::async> asyncRender;
- std::unique_ptr<uv::async> asyncInvoke;
- std::unique_ptr<uv::async> asyncTerminate;
std::unique_ptr<Worker> workers;
- const std::unique_ptr<GlyphStore> glyphStore;
- const std::unique_ptr<GlyphAtlas> glyphAtlas;
- const std::unique_ptr<SpriteAtlas> spriteAtlas;
- const std::unique_ptr<LineAtlas> lineAtlas;
- const std::unique_ptr<TexturePool> texturePool;
- const std::unique_ptr<Painter> painter;
+ std::unique_ptr<GlyphStore> glyphStore;
+ std::unique_ptr<GlyphAtlas> glyphAtlas;
+ std::unique_ptr<SpriteAtlas> spriteAtlas;
+ std::unique_ptr<LineAtlas> lineAtlas;
+ std::unique_ptr<TexturePool> texturePool;
+ std::unique_ptr<Painter> painter;
util::ptr<Sprite> sprite;
util::ptr<Style> style;
diff --git a/src/mbgl/map/map_data.hpp b/src/mbgl/map/map_data.hpp
index c2aa222463..4eaefa563d 100644
--- a/src/mbgl/map/map_data.hpp
+++ b/src/mbgl/map/map_data.hpp
@@ -2,13 +2,13 @@
#define MBGL_MAP_MAP_DATA
#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/signal.hpp>
#include <string>
#include <mutex>
#include <atomic>
#include <vector>
#include <cassert>
+#include <condition_variable>
#include <mbgl/map/mode.hpp>
#include <mbgl/map/environment.hpp>
@@ -125,22 +125,9 @@ private:
// TODO: make private
public:
- // Used to signal that rendering completed.
- public: util::Signal rendered;
-
- // TODO: document
- bool terminating = false;
-
- std::thread thread;
- bool pausing = false;
- bool isPaused = false;
- std::mutex mutexRun;
- std::condition_variable condRun;
std::mutex mutexPause;
- std::condition_variable condPause;
-
- // Stores whether the map thread has been stopped already.
- std::atomic_bool isStopped;
+ std::condition_variable condPaused;
+ std::condition_variable condResume;
using StillImageCallback = std::function<void(std::unique_ptr<const StillImage>)>;
StillImageCallback callback;
diff --git a/src/mbgl/util/run_loop.hpp b/src/mbgl/util/run_loop.hpp
index d785854e79..a192fe1799 100644
--- a/src/mbgl/util/run_loop.hpp
+++ b/src/mbgl/util/run_loop.hpp
@@ -53,6 +53,8 @@ public:
uv_loop_t* get() { return *loop; }
+ static uv::tls<RunLoop> current;
+
private:
// A movable type-erasing invokable entity wrapper. This allows to store arbitrary invokable
// things (like std::function<>, or the result of a movable-only std::bind()) in the queue.
@@ -71,8 +73,6 @@ private:
using Queue = std::queue<std::unique_ptr<Message>>;
- static uv::tls<RunLoop> current;
-
void withMutex(std::function<void()>&&);
void process();
diff --git a/src/mbgl/util/signal.cpp b/src/mbgl/util/signal.cpp
deleted file mode 100644
index 58795f7458..0000000000
--- a/src/mbgl/util/signal.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "signal.hpp"
-
-namespace mbgl {
-namespace util {
-
-void Signal::wait() {
- std::unique_lock<std::mutex> lock(mutex);
- condition.wait(lock, [this] { return status; });
- status = false;
-}
-
-void Signal::notify() {
- {
- std::lock_guard<std::mutex> lock(mutex);
- status = true;
- }
- condition.notify_all();
-}
-
-}
-}
diff --git a/src/mbgl/util/signal.hpp b/src/mbgl/util/signal.hpp
deleted file mode 100644
index fa406601b6..0000000000
--- a/src/mbgl/util/signal.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef MBGL_UTIL_SIGNAL
-#define MBGL_UTIL_SIGNAL
-
-#include <mutex>
-#include <condition_variable>
-
-namespace mbgl {
-namespace util {
-
-class Signal {
-public:
- void wait();
- void notify();
-
-private:
- bool status = false;
- std::condition_variable condition;
- std::mutex mutex;
-};
-
-}
-}
-
-#endif
diff --git a/src/mbgl/util/thread.hpp b/src/mbgl/util/thread.hpp
index 956e7718dc..036ac33c97 100644
--- a/src/mbgl/util/thread.hpp
+++ b/src/mbgl/util/thread.hpp
@@ -3,6 +3,7 @@
#include <future>
#include <thread>
+#include <atomic>
#include <functional>
#include <mbgl/util/run_loop.hpp>
@@ -52,6 +53,29 @@ public:
loop->invokeWithResult(std::bind(fn, object, args...), callback);
}
+ // Invoke object->fn(args...) in the runloop thread, and wait for the result.
+ template <class R, typename Fn, class... Args>
+ R invokeSync(Fn fn, Args&&... args) {
+ std::promise<R> promise;
+ auto bound = std::bind(fn, object, args...);
+ loop->invoke([&] { promise.set_value(bound()); } );
+ return promise.get_future().get();
+ }
+
+ // Invoke object->fn(args...) in the runloop thread, and wait for it to complete.
+ template <typename Fn, class... Args>
+ void invokeSync(Fn fn, Args&&... args) {
+ std::promise<void> promise;
+ auto bound = std::bind(fn, object, args...);
+ loop->invoke([&] { bound(); promise.set_value(); } );
+ promise.get_future().get();
+ }
+
+ // Join the thread, but call the given function repeatedly in the current thread
+ // while waiting for the join to finish. This should be immediately followed by
+ // destroying the Thread.
+ void pumpingStop(std::function<void ()>);
+
uv_loop_t* get() { return loop->get(); }
private:
@@ -67,6 +91,7 @@ private:
std::promise<void> joinable;
std::thread thread;
+ std::atomic_bool joined;
Object* object;
RunLoop* loop;
@@ -74,7 +99,8 @@ private:
template <class Object>
template <class... Args>
-Thread<Object>::Thread(const std::string& name, Args&&... args) {
+Thread<Object>::Thread(const std::string& name, Args&&... args)
+ : joined(false) {
// Note: We're using std::tuple<> to store the arguments because GCC 4.9 has a bug
// when expanding parameters packs captured in lambdas.
std::tuple<Args...> params = std::forward_as_tuple(::std::forward<Args>(args)...);
@@ -99,13 +125,21 @@ void Thread<Object>::run(P&& params, index_sequence<I...>) {
RunLoop loop_;
loop = &loop_;
- Object object_(loop_.get(), std::get<I>(std::forward<P>(params))...);
- object = &object_;
+ {
+ Object object_(loop_.get(), std::get<I>(std::forward<P>(params))...);
+ object = &object_;
- running.set_value();
+ running.set_value();
+ loop_.run();
+
+ object = nullptr;
+ }
+
+ // Run the loop again to ensure that async close callbacks have been called.
loop_.run();
joinable.get_future().get();
+ joined = true;
}
template <class Object>
@@ -115,6 +149,15 @@ Thread<Object>::~Thread() {
thread.join();
}
+template <class Object>
+void Thread<Object>::pumpingStop(std::function<void ()> cb) {
+ loop->stop();
+ joinable.set_value();
+ while (!joined) {
+ cb();
+ }
+}
+
}
}