Files
MapLibreParticleLab/vendor/maplibre-native-base/test/std/weak.cpp
2025-04-16 02:18:42 +02:00

203 lines
5.3 KiB
C++

#include "mapbox/std/weak.hpp"
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <ctime>
#include <memory>
#include <thread>
#include <vector>
TEST(WeakPtr, Lock) {
using namespace std::chrono_literals;
static std::atomic_int g_i;
struct TestLock {
void inc() { ++g_i; }
mapbox::base::WeakPtrFactory<TestLock> factory_{this};
};
auto t = std::make_unique<TestLock>();
auto weak1 = t->factory_.makeWeakPtr();
auto weak2 = t->factory_.makeWeakPtr();
auto weak3 = weak2;
std::thread thread1([&] {
auto guard = weak1.lock();
std::this_thread::sleep_for(150ms);
weak1->inc();
});
std::thread thread2([&] {
auto guard = weak2.lock();
std::this_thread::sleep_for(200ms);
weak2->inc();
});
{
auto guard = weak3.lock();
std::this_thread::sleep_for(50ms);
weak3->inc();
}
EXPECT_FALSE(weak1.expired());
EXPECT_FALSE(weak2.expired());
EXPECT_TRUE(weak1);
EXPECT_TRUE(weak2);
ASSERT_NO_THROW(t.reset()); // Should not crash.
thread1.join();
thread2.join();
EXPECT_TRUE(weak1.expired());
EXPECT_TRUE(weak2.expired());
EXPECT_FALSE(weak1);
EXPECT_FALSE(weak2);
EXPECT_EQ(g_i, 3);
}
TEST(WeakPtr, InvalidateWeakPtrs) {
using namespace std::chrono_literals;
static std::atomic_int g_i;
struct TestLock {
void inc() { ++g_i; }
mapbox::base::WeakPtrFactory<TestLock> factory_{this};
};
auto t = std::make_unique<TestLock>();
auto weak1 = t->factory_.makeWeakPtr();
auto weak2 = t->factory_.makeWeakPtr();
auto weak3 = weak2;
std::thread thread1([&] {
auto guard = weak1.lock();
std::this_thread::sleep_for(150ms);
weak1->inc();
});
std::thread thread2([&] {
auto guard = weak2.lock();
std::this_thread::sleep_for(200ms);
weak2->inc();
});
{
auto guard = weak3.lock();
std::this_thread::sleep_for(50ms);
weak3->inc();
}
EXPECT_TRUE(weak1);
EXPECT_TRUE(weak2);
EXPECT_TRUE(weak3);
t->factory_.invalidateWeakPtrs();
// All the existing weak pointer have expired.
EXPECT_FALSE(weak3);
thread1.join();
thread2.join();
EXPECT_FALSE(weak1);
EXPECT_FALSE(weak2);
EXPECT_EQ(g_i, 3);
// The newly created one is empty.
auto weak4 = t->factory_.makeWeakPtr();
EXPECT_FALSE(weak4);
}
TEST(WeakPtr, MultipleLock) {
using namespace std::chrono_literals;
using std::chrono::system_clock;
static std::atomic_int g_i;
struct TestLock {
void inc() { ++g_i; }
mapbox::base::WeakPtrFactory<TestLock> factory_{this};
};
std::time_t now = system_clock::to_time_t(system_clock::now());
struct std::tm* tm = std::localtime(&now);
++tm->tm_sec; // Wait for the next second.
auto nextSecond = system_clock::from_time_t(mktime(tm));
auto t = std::make_unique<TestLock>();
const size_t threadsCount = 50u;
std::vector<std::thread> threads;
threads.reserve(threadsCount);
for (size_t i = 0; i < threadsCount; ++i) {
std::thread thread([nextSecond, weak = t->factory_.makeWeakPtr()] {
auto guard = weak.lock();
std::this_thread::sleep_until(nextSecond);
weak->inc();
});
threads.emplace_back(std::move(thread));
}
t.reset();
for (auto& thread : threads) {
thread.join();
}
EXPECT_EQ(g_i, threadsCount);
}
TEST(WeakPtr, WeakMethod) {
using namespace std::chrono_literals;
static std::atomic_int g_i;
class Test {
public:
void increaseGlobal(int delta) { g_i += delta; }
std::function<void(int)> makeWeakIncreaseGlobal() { return factory_.makeWeakMethod(&Test::increaseGlobal); }
private:
mapbox::base::WeakPtrFactory<Test> factory_{this};
};
auto t = std::make_unique<Test>();
std::function<void(int)> weak1 = t->makeWeakIncreaseGlobal();
std::function<void(int)> weak2 = t->makeWeakIncreaseGlobal();
std::function<void(int)> weak3 = weak2;
std::thread thread1([&] { weak1(1); });
std::thread thread2([&] { weak2(10); });
std::this_thread::sleep_for(50ms);
weak3(100);
t.reset(); // Should not crash.
// The following calls are ignored.
weak1(1);
weak2(2);
weak3(3);
thread1.join();
thread2.join();
EXPECT_EQ(g_i, 111);
}
TEST(WeakPtr, WeakMethodBlock) {
// NOLINTNEXTLINE(google-build-using-namespace)
using namespace std::chrono;
using namespace std::chrono_literals;
static std::atomic_bool g_call_finished{false};
struct Test {
void block(decltype(1ms) duration) {
std::this_thread::sleep_for(duration);
g_call_finished = true;
}
mapbox::base::WeakPtrFactory<Test> factory_{this};
};
auto t = std::make_unique<Test>();
auto weak = t->factory_.makeWeakMethod(&Test::block);
auto first = high_resolution_clock::now();
std::thread thread([&] { weak(100ms); });
std::this_thread::sleep_for(10ms);
t.reset(); // Deletion is blocked until weak(100ms) call returns.
thread.join();
auto totalTime = duration_cast<milliseconds>(high_resolution_clock::now() - first);
EXPECT_TRUE(g_call_finished);
EXPECT_GE(totalTime, 100ms);
}