make sure all of our async handles are stopped before stopping the map thread

This commit is contained in:
Konstantin Käfer
2014-09-16 14:48:46 +02:00
parent d9fc7708a2
commit 6c526e5637
10 changed files with 92 additions and 56 deletions

View File

@@ -34,7 +34,13 @@ using namespace mbgl;
Map::Map(View& view)
: loop(std::make_shared<uv::loop>()),
thread(std::make_unique<uv::thread>()),
async_terminate(new uv_async_t()),
async_render(new uv_async_t()),
async_cleanup(new uv_async_t()),
view(view),
#ifndef NDEBUG
main_thread(uv_thread_self()),
#endif
transform(view),
glyphAtlas(std::make_shared<GlyphAtlas>(1024, 1024)),
spriteAtlas(std::make_shared<SpriteAtlas>(512, 512)),
@@ -50,52 +56,52 @@ Map::Map(View& view)
}
Map::~Map() {
// Clear the style first before the rest of the constructor deletes members of this object.
// This is required because members of the style reference the Map object in their destructors.
style.reset();
if (async) {
stop();
}
}
void Map::start() {
assert(uv_thread_self() == main_thread);
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;
// Setup async notifications
async_terminate = new uv_async_t();
uv_async_init(**loop, async_terminate, terminate);
uv_async_init(**loop, async_terminate.get(), terminate);
async_terminate->data = this;
async_render = new uv_async_t();
uv_async_init(**loop, async_render, render);
uv_async_init(**loop, async_render.get(), render);
async_render->data = this;
async_cleanup = new uv_async_t();
uv_async_init(**loop, async_cleanup, cleanup);
uv_async_init(**loop, async_cleanup.get(), cleanup);
async_cleanup->data = this;
uv_thread_create(*thread, [](void *arg) {
Map *map = static_cast<Map *>(arg);
#ifndef NDEBUG
map->map_thread = uv_thread_self();
#endif
#ifdef __APPLE__
pthread_setname_np("Map");
#endif
map->run();
#ifndef NDEBUG
map->map_thread = -1;
#endif
}, this);
}
void Map::stop() {
if (async_terminate != nullptr) {
uv_async_send(async_terminate);
}
assert(uv_thread_self() == main_thread);
assert(main_thread != map_thread);
uv_async_send(async_terminate.get());
uv_thread_join(*thread);
// Run the event loop once to make sure our async delete handlers are called.
uv_run(**loop, UV_RUN_ONCE);
async = false;
}
@@ -104,23 +110,36 @@ void Map::delete_async(uv_handle_t *handle) {
}
void Map::run() {
#ifndef NDEBUG
if (!async) {
map_thread = main_thread;
}
#endif
assert(uv_thread_self() == map_thread);
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) {
prepare();
render();
#ifndef NDEBUG
map_thread = -1;
#endif
}
}
void Map::rerender() {
// We only send render events if we want to continuously update the map
// (== async rendering).
if (async && async_render != nullptr) {
uv_async_send(async_render);
if (async) {
uv_async_send(async_render.get());
}
}
@@ -140,7 +159,7 @@ void Map::swapped() {
void Map::cleanup() {
if (async_cleanup != nullptr) {
uv_async_send(async_cleanup);
uv_async_send(async_cleanup.get());
}
}
@@ -153,7 +172,7 @@ void Map::cleanup(uv_async_t *async) {
void Map::render(uv_async_t *async) {
Map *map = static_cast<Map *>(async->data);
assert(uv_thread_self() == map->map_thread);
if (map->state.hasSize()) {
if (map->is_rendered.test_and_set() == false) {
@@ -174,18 +193,23 @@ void Map::render(uv_async_t *async) {
void Map::terminate(uv_async_t *async) {
// Closes all open handles on the loop. This means that the loop will automatically terminate.
Map *map = static_cast<Map *>(async->data);
assert(uv_thread_self() == map->map_thread);
// Remove all of these to make sure they are destructed in the correct thread.
map->glyphStore.reset();
map->fileSource.reset();
uv_walk(**map->loop, [](uv_handle_t *handle, void */*arg*/) {
if (!uv_is_closing(handle)) {
uv_close(handle, NULL);
}
}, NULL);
map->style.reset();
map->activeSources.clear();
uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr);
uv_close((uv_handle_t *)map->async_render.get(), nullptr);
uv_close((uv_handle_t *)map->async_terminate.get(), nullptr);
}
#pragma mark - Setup
void Map::setup() {
assert(uv_thread_self() == map_thread);
view.make_active();
painter.setup();
@@ -201,6 +225,7 @@ void Map::setStyleJSON(std::string newStyleJSON, const std::string &base) {
// TODO: Make threadsafe.
styleJSON.swap(newStyleJSON);
sprite.reset();
assert(style);
style->loadJSON((const uint8_t *)styleJSON.c_str());
fileSource->setBase(base);
glyphStore->setURL(util::mapbox::normalizeGlyphsURL(style->glyph_url, getAccessToken()));
@@ -450,6 +475,8 @@ void Map::setDefaultTransitionDuration(uint64_t duration_milliseconds) {
}
void Map::updateSources() {
assert(uv_thread_self() == map_thread);
// First, disable all existing sources.
for (const std::shared_ptr<StyleSource> &source : activeSources) {
source->enabled = false;