Asynchronous GeometryTile deletion (#2051)

Co-authored-by: Tim Sylvester <tim@wetdogweather.com>
This commit is contained in:
mwilsnd
2024-02-28 17:18:08 -05:00
committed by GitHub
parent e3cbdb63a8
commit 4fff8a5d33
80 changed files with 1180 additions and 334 deletions

View File

@@ -2,9 +2,7 @@ load("//bazel:flags.bzl", "CPP_FLAGS", "MAPLIBRE_FLAGS")
cc_library(
name = "testutils",
hdrs = [
"include/mbgl/test/util.hpp",
],
hdrs = glob(["include/mbgl/test/*.hpp"]),
strip_include_prefix = "include",
deps = [
"//vendor/googletest:gtest",

View File

@@ -91,7 +91,12 @@ TEST(Actor, DestructionBlocksOnSend) {
~TestScheduler() override { EXPECT_TRUE(waited.load()); }
void schedule(std::function<void()>) final {
std::size_t waitForEmpty(Milliseconds) override {
assert(false);
return 0;
}
void schedule(std::function<void()>&&) final {
promise.set_value();
future.wait();
std::this_thread::sleep_for(1ms);

View File

@@ -0,0 +1,49 @@
#include <mbgl/map/transform.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/test/fake_file_source.hpp>
#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/util/run_loop.hpp>
#include <memory>
#include <mbgl/test/util.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/string.hpp>
namespace mbgl {
class VectorTileTest {
public:
std::shared_ptr<FileSource> fileSource = std::make_shared<FakeFileSource>();
TransformState transformState;
util::RunLoop loop;
style::Style style{fileSource, 1};
AnnotationManager annotationManager{style};
const std::shared_ptr<ImageManager> imageManager = std::make_shared<ImageManager>();
const std::shared_ptr<GlyphManager> glyphManager = std::make_shared<GlyphManager>();
Tileset tileset{{"https://example.com"}, {0, 22}, "none"};
const std::shared_ptr<Scheduler> threadPool = Scheduler::GetBackground();
TileParameters tileParameters{1.0,
MapDebugOptions(),
transformState,
fileSource,
MapMode::Continuous,
annotationManager.makeWeakPtr(),
imageManager,
glyphManager,
0};
~VectorTileTest() {
// Ensure that deferred releases are complete before cleaning up
EXPECT_EQ(0, loop.waitForEmpty(Milliseconds::zero()));
EXPECT_EQ(0, threadPool->waitForEmpty());
}
};
} // namespace mbgl

View File

@@ -1451,11 +1451,14 @@ TEST(Map, KeepRenderData) {
test.map.getStyle().loadURL("maptiler://maps/streets");
const int iterations = 3;
const int resourcesCount = 4 /*tiles*/;
requestsCount = 0;
// Keep render data.
for (int i = 1; i <= iterations; ++i) {
test.frontend.render(test.map);
EXPECT_EQ(resourcesCount, requestsCount);
}
requestsCount = 0;
// Clear render data.
for (int i = 1; i <= iterations; ++i) {

View File

@@ -33,6 +33,8 @@ TEST(ImageManager, Basic) {
ASSERT_TRUE(stored);
EXPECT_EQ(image->image.size, stored->image.size);
}
imageManager.dumpDebugLogs();
}
TEST(ImageManager, AddRemove) {
@@ -79,7 +81,7 @@ TEST(ImageManager, RemoveReleasesBinPackRect) {
class StubImageRequestor : public ImageRequestor {
public:
StubImageRequestor(ImageManager& imageManager_)
StubImageRequestor(std::shared_ptr<ImageManager> imageManager_)
: ImageRequestor(imageManager_) {}
void onImagesAvailable(ImageMap icons,
@@ -95,8 +97,9 @@ public:
TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) {
util::RunLoop runLoop;
ImageManager imageManager;
StubImageRequestor requestor(imageManager);
auto imageManagerPtr = std::make_shared<ImageManager>();
auto& imageManager = *imageManagerPtr;
StubImageRequestor requestor(imageManagerPtr);
bool notified = false;
ImageManagerObserver observer;
@@ -123,8 +126,9 @@ TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) {
}
TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) {
ImageManager imageManager;
StubImageRequestor requestor(imageManager);
auto imageManagerPtr = std::make_shared<ImageManager>();
auto& imageManager = *imageManagerPtr;
StubImageRequestor requestor(imageManagerPtr);
bool notified = false;
requestor.imagesAvailable = [&](ImageMap, ImageMap, std::unordered_map<std::string, uint32_t>) {
@@ -160,8 +164,9 @@ public:
TEST(ImageManager, OnStyleImageMissingBeforeSpriteLoaded) {
util::RunLoop runLoop;
ImageManager imageManager;
StubImageRequestor requestor(imageManager);
auto imageManagerPtr = std::make_shared<ImageManager>();
auto& imageManager = *imageManagerPtr;
StubImageRequestor requestor(imageManagerPtr);
StubImageManagerObserver observer;
imageManager.setObserver(&observer);
@@ -203,7 +208,7 @@ TEST(ImageManager, OnStyleImageMissingBeforeSpriteLoaded) {
ASSERT_FALSE(requestor.hasPendingRequests());
// Another requestor shall not have pending requests for already obtained images.
StubImageRequestor anotherRequestor(imageManager);
StubImageRequestor anotherRequestor(imageManagerPtr);
imageManager.getImages(anotherRequestor, std::make_pair(dependencies, ++imageCorrelationID));
ASSERT_FALSE(anotherRequestor.hasPendingRequests());
@@ -215,8 +220,9 @@ TEST(ImageManager, OnStyleImageMissingBeforeSpriteLoaded) {
TEST(ImageManager, OnStyleImageMissingAfterSpriteLoaded) {
util::RunLoop runLoop;
ImageManager imageManager;
StubImageRequestor requestor(imageManager);
auto imageManagerPtr = std::make_shared<ImageManager>();
auto& imageManager = *imageManagerPtr;
StubImageRequestor requestor(imageManagerPtr);
StubImageManagerObserver observer;
imageManager.setObserver(&observer);
@@ -251,7 +257,8 @@ TEST(ImageManager, OnStyleImageMissingAfterSpriteLoaded) {
TEST(ImageManager, RemoveUnusedStyleImages) {
util::RunLoop runLoop;
ImageManager imageManager;
auto imageManagerPtr = std::make_shared<ImageManager>();
auto& imageManager = *imageManagerPtr;
StubImageManagerObserver observer;
imageManager.setObserver(&observer);
imageManager.setLoaded(true);
@@ -276,7 +283,7 @@ TEST(ImageManager, RemoveUnusedStyleImages) {
// Single requestor
{
std::unique_ptr<StubImageRequestor> requestor = std::make_unique<StubImageRequestor>(imageManager);
std::unique_ptr<StubImageRequestor> requestor = std::make_unique<StubImageRequestor>(imageManagerPtr);
imageManager.getImages(*requestor, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 0ull));
runLoop.runOnce();
EXPECT_EQ(observer.count, 1);
@@ -296,7 +303,7 @@ TEST(ImageManager, RemoveUnusedStyleImages) {
// Single requestor, exceed cache size limit.
{
std::unique_ptr<StubImageRequestor> requestor = std::make_unique<StubImageRequestor>(imageManager);
std::unique_ptr<StubImageRequestor> requestor = std::make_unique<StubImageRequestor>(imageManagerPtr);
imageManager.getImages(*requestor, std::make_pair(ImageDependencies{{"1024px", ImageType::Icon}}, 0ull));
runLoop.runOnce();
EXPECT_EQ(observer.count, 2);
@@ -311,8 +318,8 @@ TEST(ImageManager, RemoveUnusedStyleImages) {
// Multiple requestors
{
std::unique_ptr<StubImageRequestor> requestor1 = std::make_unique<StubImageRequestor>(imageManager);
std::unique_ptr<StubImageRequestor> requestor2 = std::make_unique<StubImageRequestor>(imageManager);
std::unique_ptr<StubImageRequestor> requestor1 = std::make_unique<StubImageRequestor>(imageManagerPtr);
std::unique_ptr<StubImageRequestor> requestor2 = std::make_unique<StubImageRequestor>(imageManagerPtr);
imageManager.getImages(*requestor1, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 0ull));
imageManager.getImages(*requestor2, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 1ull));
runLoop.runOnce();
@@ -330,9 +337,9 @@ TEST(ImageManager, RemoveUnusedStyleImages) {
// Multiple requestors, check that image resource is not destroyed if there
// is at least 1 requestor that uses it.
std::unique_ptr<StubImageRequestor> requestor = std::make_unique<StubImageRequestor>(imageManager);
std::unique_ptr<StubImageRequestor> requestor = std::make_unique<StubImageRequestor>(imageManagerPtr);
{
std::unique_ptr<StubImageRequestor> requestor1 = std::make_unique<StubImageRequestor>(imageManager);
std::unique_ptr<StubImageRequestor> requestor1 = std::make_unique<StubImageRequestor>(imageManagerPtr);
imageManager.getImages(
*requestor,
std::make_pair(ImageDependencies{{"missing", ImageType::Icon}, {"1024px", ImageType::Icon}}, 0ull));

View File

@@ -62,8 +62,9 @@ public:
TransformState transformState;
Style style{fileSource, 1};
AnnotationManager annotationManager{style};
ImageManager imageManager;
GlyphManager glyphManager;
std::shared_ptr<ImageManager> imageManager = std::make_shared<ImageManager>();
std::shared_ptr<GlyphManager> glyphManager = std::make_shared<GlyphManager>();
std::shared_ptr<Scheduler> threadPool = Scheduler::GetBackground();
TileParameters tileParameters(MapMode mapMode = MapMode::Continuous) {
return {1.0,
@@ -87,6 +88,8 @@ public:
transformState = transform.getState();
}
~SourceTest() { threadPool->waitForEmpty(); }
void run() { loop.run(); }
void end() { loop.stop(); }
@@ -167,7 +170,7 @@ TEST(Source, RasterTileEmpty) {
FAIL() << "Should never be called";
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -203,7 +206,7 @@ TEST(Source, RasterDEMTileEmpty) {
FAIL() << "Should never be called";
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -241,7 +244,7 @@ TEST(Source, VectorTileEmpty) {
FAIL() << "Should never be called";
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -276,7 +279,7 @@ TEST(Source, RasterTileFail) {
test.end();
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -311,7 +314,7 @@ TEST(Source, RasterDEMTileFail) {
test.end();
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -348,7 +351,7 @@ TEST(Source, VectorTileFail) {
test.end();
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -384,7 +387,7 @@ TEST(Source, RasterTileCorrupt) {
test.end();
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -421,7 +424,7 @@ TEST(Source, RasterDEMTileCorrupt) {
test.end();
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -458,7 +461,7 @@ TEST(Source, VectorTileCorrupt) {
test.end();
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -492,7 +495,7 @@ TEST(Source, RasterTileCancel) {
FAIL() << "Should never be called";
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -526,7 +529,7 @@ TEST(Source, RasterDEMTileCancel) {
FAIL() << "Should never be called";
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -562,7 +565,7 @@ TEST(Source, VectorTileCancel) {
FAIL() << "Should never be called";
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -607,7 +610,7 @@ TEST(Source, RasterTileAttribution) {
source.setObserver(&test.styleObserver);
source.loadDescription(*test.fileSource);
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
test.run();
@@ -648,7 +651,7 @@ TEST(Source, RasterDEMTileAttribution) {
source.setObserver(&test.styleObserver);
source.loadDescription(*test.fileSource);
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
test.run();
@@ -741,7 +744,7 @@ TEST(Source, CustomGeometrySourceSetTileData) {
FAIL() << "Should never be called";
};
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());
@@ -778,8 +781,8 @@ public:
MOCK_METHOD1(tileSetNecessity, void(TileNecessity));
MOCK_METHOD1(tileSetMinimumUpdateInterval, void(Duration));
explicit FakeTileSource(Immutable<style::Source::Impl> impl_)
: RenderTileSetSource(std::move(impl_)) {}
explicit FakeTileSource(Immutable<style::Source::Impl> impl_, std::shared_ptr<Scheduler> threadPool_)
: RenderTileSetSource(std::move(impl_), std::move(threadPool_)) {}
void updateInternal(const Tileset& tileset,
const std::vector<Immutable<style::LayerProperties>>& layers,
const bool needsRendering,
@@ -816,7 +819,7 @@ TEST(Source, InvisibleSourcesTileNecessity) {
VectorSource initialized("source", Tileset{{"tiles"}});
initialized.loadDescription(*test.fileSource);
FakeTileSource renderTilesetSource{initialized.baseImpl};
FakeTileSource renderTilesetSource{initialized.baseImpl, test.threadPool};
RenderSource* renderSource = &renderTilesetSource;
LineLayer layer("id", "source");
Immutable<LayerProperties> layerProperties = makeMutable<LineLayerProperties>(
@@ -839,7 +842,7 @@ TEST(Source, SourceMinimumUpdateInterval) {
VectorSource initialized("source", Tileset{{"tiles"}});
initialized.loadDescription(*test.fileSource);
FakeTileSource renderTilesetSource{initialized.baseImpl};
FakeTileSource renderTilesetSource{initialized.baseImpl, test.threadPool};
RenderSource* renderSource = &renderTilesetSource;
LineLayer layer("id", "source");
Immutable<LayerProperties> layerProperties = makeMutable<LineLayerProperties>(
@@ -882,8 +885,8 @@ TEST(Source, RenderTileSetSourceUpdate) {
class FakeRenderTileSetSource : public RenderTileSetSource {
public:
explicit FakeRenderTileSetSource(Immutable<style::Source::Impl> impl_)
: RenderTileSetSource(std::move(impl_)) {}
explicit FakeRenderTileSetSource(Immutable<style::Source::Impl> impl_, std::shared_ptr<Scheduler> threadPool_)
: RenderTileSetSource(std::move(impl_), std::move(threadPool_)) {}
MOCK_METHOD0(mockedUpdateInternal, void());
@@ -903,7 +906,7 @@ TEST(Source, RenderTileSetSourceUpdate) {
VectorSource initialized("source", Tileset{{"tiles"}});
initialized.loadDescription(*test.fileSource);
FakeRenderTileSetSource renderTilesetSource{initialized.baseImpl};
FakeRenderTileSetSource renderTilesetSource{initialized.baseImpl, test.threadPool};
LineLayer layer("id", "source");
Immutable<LayerProperties> layerProperties = makeMutable<LineLayerProperties>(
@@ -969,7 +972,7 @@ TEST(Source, GeoJSONSourceTilesAfterDataReset) {
auto geoJSONData = GeoJSONData::create(mapbox::geojson::parse(
R"({"geometry": {"type": "Point", "coordinates": [1.1, 1.1]}, "type": "Feature", "properties": {}})"));
source.setGeoJSONData(geoJSONData);
RenderGeoJSONSource renderSource{staticImmutableCast<GeoJSONSource::Impl>(source.baseImpl)};
RenderGeoJSONSource renderSource{staticImmutableCast<GeoJSONSource::Impl>(source.baseImpl), test.threadPool};
CircleLayer layer("id", "source");
Immutable<LayerProperties> layerProperties = makeMutable<CircleLayerProperties>(
@@ -1026,7 +1029,7 @@ TEST(Source, SetMaxParentOverscaleFactor) {
ASSERT_EQ(3, *source.getMaxOverscaleFactorForParentTiles());
source.loadDescription(*test.fileSource);
auto renderSource = RenderSource::create(source.baseImpl);
auto renderSource = RenderSource::create(source.baseImpl, test.threadPool);
renderSource->setObserver(&test.renderSourceObserver);
renderSource->update(source.baseImpl, layers, true, true, test.tileParameters());

View File

@@ -27,8 +27,8 @@ public:
util::RunLoop loop;
style::Style style{fileSource, 1};
AnnotationManager annotationManager{style};
ImageManager imageManager;
GlyphManager glyphManager;
std::shared_ptr<ImageManager> imageManager = std::make_shared<ImageManager>();
std::shared_ptr<GlyphManager> glyphManager = std::make_shared<GlyphManager>();
TileParameters tileParameters{1.0,
MapDebugOptions(),

View File

@@ -27,8 +27,8 @@ public:
util::RunLoop loop;
style::Style style{fileSource, 1};
AnnotationManager annotationManager{style};
ImageManager imageManager;
GlyphManager glyphManager;
std::shared_ptr<ImageManager> imageManager = std::make_shared<ImageManager>();
std::shared_ptr<GlyphManager> glyphManager = std::make_shared<GlyphManager>();
Tileset tileset{{"https://example.com"}, {0, 22}, "none"};
TileParameters tileParameters{1.0,

View File

@@ -21,8 +21,8 @@ public:
util::RunLoop loop;
style::Style style{fileSource, 1};
AnnotationManager annotationManager{style};
ImageManager imageManager;
GlyphManager glyphManager;
std::shared_ptr<ImageManager> imageManager = std::make_shared<ImageManager>();
std::shared_ptr<GlyphManager> glyphManager = std::make_shared<GlyphManager>();
Tileset tileset{{"https://example.com"}, {0, 22}, "none"};
TileParameters tileParameters{1.0,

View File

@@ -21,8 +21,8 @@ public:
util::RunLoop loop;
style::Style style{fileSource, 1};
AnnotationManager annotationManager{style};
ImageManager imageManager;
GlyphManager glyphManager;
std::shared_ptr<ImageManager> imageManager = std::make_shared<ImageManager>();
std::shared_ptr<GlyphManager> glyphManager = std::make_shared<GlyphManager>();
Tileset tileset{{"https://example.com"}, {0, 22}, "none"};
TileParameters tileParameters{1.0,

View File

@@ -7,6 +7,7 @@
#include <mbgl/tile/vector_tile.hpp>
#include <mbgl/tile/vector_tile_data.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/map/transform.hpp>
@@ -16,34 +17,16 @@
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/style/layers/symbol_layer.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/test/vector_tile_test.hpp>
#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/identity.hpp>
#include <memory>
using namespace mbgl;
class VectorTileTest {
public:
std::shared_ptr<FileSource> fileSource = std::make_shared<FakeFileSource>();
TransformState transformState;
util::RunLoop loop;
style::Style style{fileSource, 1};
AnnotationManager annotationManager{style};
ImageManager imageManager;
GlyphManager glyphManager;
Tileset tileset{{"https://example.com"}, {0, 22}, "none"};
TileParameters tileParameters{1.0,
MapDebugOptions(),
transformState,
fileSource,
MapMode::Continuous,
annotationManager.makeWeakPtr(),
imageManager,
glyphManager,
0};
};
namespace {
class VectorTileMock : public VectorTile {
public:
@@ -51,16 +34,20 @@ public:
std::string sourceID_,
const TileParameters& parameters,
const Tileset& tileset)
: VectorTile(id_, sourceID_, parameters, tileset) {
: VectorTile(id_, std::move(sourceID_), parameters, tileset) {
renderable = true;
}
util::SimpleIdentity uniqueId;
};
} // namespace
TEST(TileCache, Smoke) {
VectorTileTest test;
TileCache cache(1);
OverscaledTileID id(0, 0, 0);
std::unique_ptr<Tile> tile = std::make_unique<VectorTileMock>(id, "source", test.tileParameters, test.tileset);
TileCache cache(Scheduler::GetBackground(), 1);
const OverscaledTileID id(0, 0, 0);
auto tile = std::make_unique<VectorTileMock>(id, "source", test.tileParameters, test.tileset);
cache.add(id, std::move(tile));
EXPECT_TRUE(cache.has(id));
@@ -70,18 +57,36 @@ TEST(TileCache, Smoke) {
TEST(TileCache, Issue15926) {
VectorTileTest test;
TileCache cache(2);
OverscaledTileID id0(0, 0, 0);
OverscaledTileID id1(1, 0, 0);
std::unique_ptr<Tile> tile1 = std::make_unique<VectorTileMock>(id0, "source", test.tileParameters, test.tileset);
std::unique_ptr<Tile> tile2 = std::make_unique<VectorTileMock>(id0, "source", test.tileParameters, test.tileset);
std::unique_ptr<Tile> tile3 = std::make_unique<VectorTileMock>(id1, "source", test.tileParameters, test.tileset);
TileCache cache(test.threadPool, 2);
const OverscaledTileID id0(0, 0, 0);
const OverscaledTileID id1(1, 0, 0);
auto tile1 = std::make_unique<VectorTileMock>(id0, "source", test.tileParameters, test.tileset);
auto tile2 = std::make_unique<VectorTileMock>(id0, "source", test.tileParameters, test.tileset);
auto tile3 = std::make_unique<VectorTileMock>(id1, "source", test.tileParameters, test.tileset);
auto tile4 = std::make_unique<VectorTileMock>(id0, "source", test.tileParameters, test.tileset);
const auto tile1Id = tile1->uniqueId;
// add
cache.add(id0, std::move(tile1));
EXPECT_TRUE(cache.has(id0));
// adding a key already present doesn't replace the existing item
cache.add(id0, std::move(tile2));
EXPECT_EQ(tile1Id, static_cast<VectorTileMock*>(cache.get(id0))->uniqueId);
// Evict on add
cache.setSize(1);
cache.add(id1, std::move(tile3));
EXPECT_FALSE(cache.has(id0));
EXPECT_TRUE(cache.has(id1));
// Evict due to size limit change
cache.setSize(2);
cache.add(id0, std::move(tile4));
EXPECT_TRUE(cache.has(id0));
EXPECT_TRUE(cache.has(id1));
cache.setSize(1);
// older item should be evicted
EXPECT_TRUE(cache.has(id0));
EXPECT_FALSE(cache.has(id1));
}

View File

@@ -15,35 +15,13 @@
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/test/vector_tile_test.hpp>
#include <mbgl/text/glyph_manager.hpp>
#include <memory>
using namespace mbgl;
class VectorTileTest {
public:
std::shared_ptr<FileSource> fileSource = std::make_shared<FakeFileSource>(ResourceOptions::Default(),
ClientOptions());
TransformState transformState;
util::RunLoop loop;
style::Style style{fileSource, 1};
AnnotationManager annotationManager{style};
ImageManager imageManager;
GlyphManager glyphManager;
Tileset tileset{{"https://example.com"}, {0, 22}, "none"};
TileParameters tileParameters{1.0,
MapDebugOptions(),
transformState,
fileSource,
MapMode::Continuous,
annotationManager.makeWeakPtr(),
imageManager,
glyphManager,
0};
};
TEST(VectorTile, setError) {
VectorTileTest test;
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset);

View File

@@ -2,6 +2,7 @@
#include <mbgl/actor/actor_ref.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/platform/settings.hpp>
#include <mbgl/test/util.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
@@ -134,6 +135,11 @@ TEST(Thread, Concurrency) {
unsigned numMessages = 100000;
std::atomic_uint completed(numMessages);
auto& settings = platform::Settings::getInstance();
if (!settings.get(platform::EXPERIMENTAL_THREAD_PRIORITY_WORKER).getDouble()) {
settings.set(platform::EXPERIMENTAL_THREAD_PRIORITY_WORKER, 0.5);
}
Actor<TestWorker> poolWorker(Scheduler::GetBackground());
auto poolWorkerRef = poolWorker.self();
@@ -328,3 +334,108 @@ TEST(Thread, DeleteBeforeChildStarts) {
// Should process the queue before destruction.
ASSERT_TRUE(flag);
}
TEST(Thread, PoolWait) {
auto pool = Scheduler::GetBackground();
constexpr int threadCount = 10;
for (int i = 0; i < threadCount; ++i) {
pool->schedule([&] { std::this_thread::sleep_for(Milliseconds(100)); });
}
EXPECT_EQ(0, pool->waitForEmpty());
}
TEST(Thread, PoolWaitRecursiveAdd) {
auto pool = Scheduler::GetBackground();
pool->schedule([&] {
// Scheduled tasks can add more tasks
pool->schedule([&] {
std::this_thread::sleep_for(Milliseconds(10));
pool->schedule([&] { std::this_thread::sleep_for(Milliseconds(10)); });
});
std::this_thread::sleep_for(Milliseconds(10));
});
EXPECT_EQ(0, pool->waitForEmpty());
}
TEST(Thread, PoolWaitAdd) {
auto pool = Scheduler::GetBackground();
auto seq = Scheduler::GetSequenced();
// add new tasks every few milliseconds
std::atomic<bool> addActive{true};
std::atomic<int> added{0};
std::atomic<int> executed{0};
seq->schedule([&] {
while (addActive) {
pool->schedule([&] { executed++; });
added++;
}
});
// Wait be sure some are added
while (added < 1) {
std::this_thread::sleep_for(Milliseconds(10));
}
// Add an item that should take long enough to be confident that
// more items would be added by the sequential task if not blocked
pool->schedule([&] { std::this_thread::sleep_for(Milliseconds(100)); });
EXPECT_EQ(0, pool->waitForEmpty());
addActive = false;
EXPECT_EQ(0, pool->waitForEmpty());
}
TEST(Thread, PoolWaitTimeout) {
auto pool = Scheduler::GetBackground();
std::mutex mutex;
{
std::lock_guard<std::mutex> outerLock(mutex);
pool->schedule([&] { std::lock_guard<std::mutex> innerLock(mutex); });
// should always time out
EXPECT_EQ(1, pool->waitForEmpty(Milliseconds(100)));
}
EXPECT_EQ(0, pool->waitForEmpty());
}
TEST(Thread, PoolWaitException) {
auto pool = Scheduler::GetBackground();
std::atomic<int> caught{0};
pool->setExceptionHandler([&](const auto) { caught++; });
constexpr int threadCount = 3;
for (int i = 0; i < threadCount; ++i) {
pool->schedule([=] {
std::this_thread::sleep_for(Milliseconds(i));
if (i & 1) {
throw std::runtime_error("test");
} else {
throw 1;
}
});
}
// Exceptions shouldn't cause deadlocks by, e.g., abandoning locks.
EXPECT_EQ(0, pool->waitForEmpty());
EXPECT_EQ(threadCount, caught);
}
#if defined(NDEBUG)
TEST(Thread, WrongThread) {
auto pool = Scheduler::GetBackground();
// Asserts in debug builds, silently ignored in release.
pool->schedule([&] { EXPECT_EQ(0, pool->waitForEmpty()); });
EXPECT_EQ(0, pool->waitForEmpty());
}
#endif