Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Hang on each tile load — VectorTileData, TileWorker::parseLayer #3636

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions include/mbgl/util/run_loop.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#ifndef MBGL_UTIL_RUN_LOOP
#define MBGL_UTIL_RUN_LOOP

#include <mbgl/platform/log.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/util.hpp>
#include <mbgl/util/thread_context.hpp>
#include <mbgl/util/work_task.hpp>
#include <mbgl/util/work_request.hpp>

Expand Down Expand Up @@ -139,8 +142,33 @@ class RunLoop : private util::noncopyable {
// If the task has completed and the after callback has executed, this will
// do nothing.
void cancel() override {
#if defined(DEBUG)
auto now = Clock::now();

if (!tryCancel()) {
std::lock_guard<std::recursive_mutex> lock(mutex);
*canceled = true;

using std::chrono::duration_cast;
long elapsed = duration_cast<Milliseconds>(Clock::now() - now).count();

if (ThreadContext::currentlyOn(ThreadType::Map)) {
Log::Warning(mbgl::Event::General, "Map thread blocked for %ld ms.", elapsed);
}
};
#else
std::lock_guard<std::recursive_mutex> lock(mutex);
*canceled = true;
#endif
}

bool tryCancel() override {
if (mutex.try_lock()) {
*canceled = true;
mutex.unlock();
return true;
}
return false;
}

private:
Expand Down
2 changes: 2 additions & 0 deletions include/mbgl/util/work_request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class WorkRequest : public util::noncopyable {
WorkRequest(Task);
~WorkRequest();

bool tryCancel();

private:
std::shared_ptr<WorkTask> task;
};
Expand Down
1 change: 1 addition & 0 deletions include/mbgl/util/work_task.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class WorkTask : private util::noncopyable {

virtual void operator()() = 0;
virtual void cancel() = 0;
virtual bool tryCancel() = 0;
};

} // namespace mbgl
Expand Down
19 changes: 16 additions & 3 deletions src/mbgl/map/raster_tile_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ RasterTileData::RasterTileData(const TileID& id_,
}

RasterTileData::~RasterTileData() {
cancel();
assert(tryCancel());
workRequest.reset();
}

void RasterTileData::request(const std::string& url,
Expand Down Expand Up @@ -56,7 +57,9 @@ void RasterTileData::request(const std::string& url,

workRequest.reset();
workRequest = worker.parseRasterTile(std::make_unique<RasterBucket>(texturePool), res.data, [this, callback] (RasterTileParseResult result) {
WorkCompletedNotifier notifier(this);
workRequest.reset();

if (state != State::loaded) {
return;
}
Expand All @@ -80,10 +83,20 @@ Bucket* RasterTileData::getBucket(StyleLayer const&) {
return bucket.get();
}

void RasterTileData::cancel() {
bool RasterTileData::tryCancel(bool force) {
if (state != State::obsolete) {
state = State::obsolete;
}

req = nullptr;
workRequest.reset();

if (force) {
workRequest.reset();
}

if (workRequest) {
return workRequest->tryCancel();
}

return true;
}
2 changes: 1 addition & 1 deletion src/mbgl/map/raster_tile_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class RasterTileData : public TileData {
void request(const std::string& url,
const Callback& callback);

void cancel() override;
bool tryCancel(bool force = false) override;

Bucket* getBucket(StyleLayer const &layer_desc) override;

Expand Down
24 changes: 14 additions & 10 deletions src/mbgl/map/source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,11 @@ TileData::State Source::addTile(const TileID& tileID, const StyleUpdateParameter

// If we don't find working tile data, we're just going to load it.
if (type == SourceType::Raster) {
auto tileData = std::make_shared<RasterTileData>(normalizedID,
parameters.texturePool,
parameters.worker);
auto tileData = std::shared_ptr<RasterTileData>(new RasterTileData{
normalizedID,
parameters.texturePool,
parameters.worker
}, [this](TileData* data) { tileDataDeleter.add(data); });
tileData->request(util::templateTileURL(info->tiles.at(0), normalizedID, parameters.pixelRatio), callback);
newTile->data = tileData;
} else {
Expand All @@ -286,12 +288,14 @@ TileData::State Source::addTile(const TileID& tileID, const StyleUpdateParameter
return TileData::State::invalid;
}

newTile->data = std::make_shared<VectorTileData>(normalizedID,
std::move(monitor),
id,
parameters.style,
parameters.mode,
callback);
newTile->data = std::shared_ptr<VectorTileData>(new VectorTileData{
normalizedID,
std::move(monitor),
id,
parameters.style,
parameters.mode,
callback
}, [this](TileData* data) { tileDataDeleter.add(data); });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why move from std::make_shared to new?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is sad, but std::make_shared doesn't support custom deleter. :(

}

tileDataMap.emplace(newTile->data->id, newTile->data);
Expand Down Expand Up @@ -485,7 +489,7 @@ bool Source::update(const StyleUpdateParameters& parameters) {
bool obsolete = retain_data.find(tile->id) == retain_data.end();
if (obsolete) {
if (!tileCache.has(tile->id.normalized().to_uint64())) {
tile->cancel();
tile->tryCancel();
}
return true;
} else {
Expand Down
3 changes: 3 additions & 0 deletions src/mbgl/map/source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define MBGL_MAP_SOURCE

#include <mbgl/map/tile_cache.hpp>
#include <mbgl/map/tile_data_deleter.hpp>
#include <mbgl/map/source_info.hpp>

#include <mbgl/util/mat4.hpp>
Expand Down Expand Up @@ -111,6 +112,8 @@ class Source : private util::noncopyable {

Observer nullObserver;
Observer* observer = &nullObserver;

TileDataDeleter tileDataDeleter;
};

} // namespace mbgl
Expand Down
32 changes: 30 additions & 2 deletions src/mbgl/map/tile_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ class DebugBucket;

class TileData : private util::noncopyable {
public:
class Observer {
public:
virtual ~Observer() = default;

virtual void onTileDataWorkCompleted(TileData*) {};
};

// initial:
// Initial state, only used when the TileData object is created.
//
Expand Down Expand Up @@ -73,8 +80,11 @@ class TileData : private util::noncopyable {
TileData(const TileID&);
virtual ~TileData();

// Mark this tile as no longer needed and cancel any pending work.
virtual void cancel() = 0;
// Mark this tile as no longer needed and cancel any pending work. Returns
// true if the work was canceled, false otherwise. Canceling is not always
// possible because in some circumstances it might block. Force will force
// the cancellation and always return true, but it can potentially block.
virtual bool tryCancel(bool force = false) = 0;

virtual Bucket* getBucket(const StyleLayer&) = 0;

Expand All @@ -92,6 +102,10 @@ class TileData : private util::noncopyable {

void dumpDebugLogs() const;

void setObserver(Observer* observer_) {
observer = observer_;
}

const TileID id;
optional<SystemTimePoint> modified;
optional<SystemTimePoint> expires;
Expand All @@ -100,7 +114,21 @@ class TileData : private util::noncopyable {
std::unique_ptr<DebugBucket> debugBucket;

protected:
class WorkCompletedNotifier {
public:
WorkCompletedNotifier(TileData* data_) : data(data_) {}
~WorkCompletedNotifier() {
data->observer->onTileDataWorkCompleted(data);
}

private:
TileData* data;
};

std::atomic<State> state;

Observer nullObserver;
Observer* observer = &nullObserver;
};

} // namespace mbgl
Expand Down
34 changes: 34 additions & 0 deletions src/mbgl/map/tile_data_deleter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <mbgl/map/tile_data_deleter.hpp>

#include <mbgl/map/tile_data.hpp>

namespace mbgl {

TileDataDeleter::~TileDataDeleter() {
// Force the canceling of all pending TileData
// before destroying. We have assert()s on Debug
// mode on TileData to make sure it doesn't block
// at the destructor unless explicitly canceled.
for (auto& entry : tileDataMap) {
entry.first->tryCancel(true);
}
}

void TileDataDeleter::add(TileData* data) {
std::unique_ptr<TileData> tileData(data);

// This custom deleter will try to cancel the task if it is
// non-blocking, otherwise we keep it and subscribe to the
// event when the work is completed. When the event triggers
// we delete the TileData because this time it won't block.
if (!tileData->tryCancel()) {
data->setObserver(this);
tileDataMap.emplace(data, std::move(tileData));
}
}

void TileDataDeleter::onTileDataWorkCompleted(TileData* data) {
tileDataMap.erase(data);
}

} // namespace mbgl
29 changes: 29 additions & 0 deletions src/mbgl/map/tile_data_deleter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef MBGL_MAP_TILE_DATA_DELETER
#define MBGL_MAP_TILE_DATA_DELETER

#include <mbgl/map/tile_data.hpp>
#include <mbgl/util/noncopyable.hpp>

#include <memory>
#include <unordered_map>

namespace mbgl {

class TileData;

class TileDataDeleter : public TileData::Observer, private util::noncopyable {
public:
virtual ~TileDataDeleter();

void add(TileData*);

// TileData::Observer implementation.
void onTileDataWorkCompleted(TileData*) override;

private:
std::unordered_map<TileData*, std::unique_ptr<TileData>> tileDataMap;
};

} // namespace mbgl

#endif
42 changes: 34 additions & 8 deletions src/mbgl/map/vector_tile_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ VectorTileData::VectorTileData(const TileID& id_,
*style_.glyphStore,
state,
mode_),
monitor(std::move(monitor_))
monitor(std::move(monitor_)),
delayedRedoPlacement([this]{ delayedRedoPlacementFunction(); })
{
state = State::loading;
tileRequest = monitor->monitorTile([callback, this](std::exception_ptr err,
Expand Down Expand Up @@ -59,6 +60,7 @@ VectorTileData::VectorTileData(const TileID& id_,
// request in case there is one.
workRequest.reset();
workRequest = worker.parseGeometryTile(tileWorker, style.getLayers(), std::move(tile), targetConfig, [callback, this, config = targetConfig] (TileParseResult result) {
WorkCompletedNotifier notifier(this);
workRequest.reset();
if (state == State::obsolete) {
return;
Expand Down Expand Up @@ -88,7 +90,8 @@ VectorTileData::VectorTileData(const TileID& id_,
}

VectorTileData::~VectorTileData() {
cancel();
assert(tryCancel());
workRequest.reset();
}

bool VectorTileData::parsePending(std::function<void(std::exception_ptr)> callback) {
Expand All @@ -97,8 +100,8 @@ bool VectorTileData::parsePending(std::function<void(std::exception_ptr)> callba
return false;
}

workRequest.reset();
workRequest = worker.parsePendingGeometryTileLayers(tileWorker, targetConfig, [this, callback, config = targetConfig] (TileParseResult result) {
WorkCompletedNotifier notifier(this);
workRequest.reset();
if (state == State::obsolete) {
return;
Expand Down Expand Up @@ -153,8 +156,8 @@ void VectorTileData::redoPlacement(const PlacementConfig newConfig, const std::f
}

void VectorTileData::redoPlacement(const std::function<void()>& callback) {
workRequest.reset();
workRequest = worker.redoPlacement(tileWorker, buckets, targetConfig, [this, callback, config = targetConfig] {
auto redoPlacementCallback = [this, callback, config = targetConfig] {
WorkCompletedNotifier notifier(this);
workRequest.reset();

// Persist the configuration we just placed so that we can later check whether we need to
Expand All @@ -172,13 +175,36 @@ void VectorTileData::redoPlacement(const std::function<void()>& callback) {
} else {
callback();
}
});
};

// We overwrite any existing function as
// we only care about the last placement.
delayedRedoPlacementFunction = [this, redoPlacementCallback] {
if (workRequest) {
delayedRedoPlacement.send();
} else {
workRequest = worker.redoPlacement(
tileWorker, buckets, targetConfig, redoPlacementCallback);
delayedRedoPlacementFunction = nullptr;
}
};

delayedRedoPlacementFunction();
}

void VectorTileData::cancel() {
bool VectorTileData::tryCancel(bool force) {
state = State::obsolete;
tileRequest.reset();
workRequest.reset();

if (force) {
workRequest.reset();
}

if (workRequest) {
return workRequest->tryCancel();
}

return true;
}

} // namespace mbgl
Loading