#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _USE_MATH_DEFINES #include #include // Check libuv library version. const static bool uv_version_check = []() { const unsigned int version = uv_version(); const unsigned int major = (version >> 16) & 0xFF; const unsigned int minor = (version >> 8) & 0xFF; const unsigned int patch = version & 0xFF; #ifndef UV_VERSION_PATCH // 0.10 doesn't have UV_VERSION_PATCH defined, so we "fake" it by using the library patch level. const unsigned int UV_VERSION_PATCH = version & 0xFF; #endif if (major != UV_VERSION_MAJOR || minor != UV_VERSION_MINOR || patch != UV_VERSION_PATCH) { throw std::runtime_error(mbgl::util::sprintf<96>( "libuv version mismatch: headers report %d.%d.%d, but library reports %d.%d.%d", UV_VERSION_MAJOR, UV_VERSION_MINOR, UV_VERSION_PATCH, major, minor, patch)); } return true; }(); #include // Check zlib library version. const static bool zlib_version_check = []() { const char *const version = zlibVersion(); if (version[0] != ZLIB_VERSION[0]) { throw std::runtime_error(mbgl::util::sprintf<96>( "zlib version mismatch: headers report %s, but library reports %s", ZLIB_VERSION, version)); } return true; }(); #include // Check sqlite3 library version. const static bool sqlite_version_check = []() { if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) { throw std::runtime_error(mbgl::util::sprintf<96>( "sqlite3 libversion mismatch: headers report %d, but library reports %d", SQLITE_VERSION_NUMBER, sqlite3_libversion_number())); } if (strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID) != 0) { throw std::runtime_error(mbgl::util::sprintf<256>( "sqlite3 sourceid mismatch: headers report \"%s\", but library reports \"%s\"", SQLITE_SOURCE_ID, sqlite3_sourceid())); } return true; }(); using namespace mbgl; Map::Map(View& view_, FileSource& fileSource_) : loop(util::make_unique()), view(view_), #ifndef NDEBUG mainThread(std::this_thread::get_id()), #endif transform(view_), fileSource(fileSource_), glyphAtlas(util::make_unique(1024, 1024)), spriteAtlas(util::make_unique(512, 512)), texturePool(std::make_shared()), painter(util::make_unique(*spriteAtlas, *glyphAtlas)) { view.initialize(this); // Make sure that we're doing an initial drawing in all cases. isClean.clear(); isRendered.clear(); isSwapped.test_and_set(); } Map::~Map() { if (async) { stop(); } // Explicitly reset all pointers. activeSources.clear(); sprite.reset(); glyphStore.reset(); style.reset(); texturePool.reset(); workers.reset(); uv_run(**loop, UV_RUN_DEFAULT); } uv::worker &Map::getWorker() { if (!workers) { workers = util::make_unique(**loop, 4, "Tile Worker"); } return *workers; } void Map::start() { assert(std::this_thread::get_id() == mainThread); assert(!async); // When starting map rendering in another thread, we perform async/continuously // updated rendering. Only in these cases, we attach the async handlers. async = true; // Reset the flag. isStopped = false; // Setup async notifications asyncTerminate = util::make_unique(**loop, [this]() { assert(std::this_thread::get_id() == mapThread); // Remove all of these to make sure they are destructed in the correct thread. glyphStore.reset(); style.reset(); workers.reset(); activeSources.clear(); // Closes all open handles on the loop. This means that the loop will automatically terminate. asyncCleanup.reset(); asyncRender.reset(); asyncTerminate.reset(); }); asyncRender = util::make_unique(**loop, [this]() { assert(std::this_thread::get_id() == mapThread); if (state.hasSize()) { if (isRendered.test_and_set() == false) { prepare(); if (isClean.test_and_set() == false) { render(); isSwapped.clear(); view.swap(); } else { // We set the rendered flag in the test above, so we have to reset it // now that we're not actually rendering because the map is clean. isRendered.clear(); } } } }); asyncCleanup = util::make_unique(**loop, [this]() { assert(painter); painter->cleanup(); }); thread = std::thread([this]() { #ifndef NDEBUG mapThread = std::this_thread::get_id(); #endif #ifdef __APPLE__ pthread_setname_np("Map"); #endif run(); #ifndef NDEBUG mapThread = std::thread::id(); #endif // Make sure that the stop() function knows when to stop invoking the callback function. isStopped = true; view.notify(); }); } void Map::stop(stop_callback cb, void *data) { assert(std::this_thread::get_id() == mainThread); assert(mainThread != mapThread); assert(async); asyncTerminate->send(); if (cb) { // Wait until the render thread stopped. We are using this construct instead of plainly // relying on the thread_join because the system might need to run things in the current // thread that is required for the render thread to terminate correctly. This is for example // 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 (!isStopped) { cb(data); } } // If a callback function was provided, this should return immediately because the thread has // already finished executing. thread.join(); async = false; } void Map::run() { #ifndef NDEBUG if (!async) { mapThread = mainThread; } #endif assert(std::this_thread::get_id() == mapThread); setup(); prepare(); uv_run(**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); // If the map rendering wasn't started asynchronously, we perform one render // *after* all events have been processed. if (!async) { render(); #ifndef NDEBUG mapThread = std::thread::id(); #endif } } void Map::rerender() { // We only send render events if we want to continuously update the map // (== async rendering). if (async) { asyncRender->send(); } } void Map::update() { isClean.clear(); rerender(); } bool Map::needsSwap() { return isSwapped.test_and_set() == false; } void Map::swapped() { isRendered.clear(); rerender(); } void Map::cleanup() { if (asyncCleanup != nullptr) { asyncCleanup->send(); } } void Map::terminate() { assert(painter); painter->terminate(); } void Map::setReachability(bool reachable) { // Note: This function may be called from *any* thread. if (reachable) { fileSource.prepare([&]() { fileSource.retryAllPending(); }); } } #pragma mark - Setup void Map::setup() { assert(std::this_thread::get_id() == mapThread); assert(painter); view.make_active(); painter->setup(); view.make_inactive(); } void Map::setStyleURL(const std::string &url) { // TODO: Make threadsafe. styleURL = url; } void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) { // TODO: Make threadsafe. styleJSON.swap(newStyleJSON); sprite.reset(); if (!style) { style = std::make_shared