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,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