1
0
Fork 0
mirror of https://gitlab.com/niansa/libcrosscoro.git synced 2025-03-06 20:53:32 +01:00
libcrosscoro/test/test_shared_mutex.cpp
Josh Baldwin 475bcf6d8b
std::shared_ptr<executor_type> for coro::shared_mutex (#86)
* std::shared_ptr<executor_type> for coro::shared_mutex

* implement remaining types that leverage executor or io_scheduler
2021-05-22 22:36:57 -06:00

160 lines
4.7 KiB
C++

#include "catch.hpp"
#include <coro/coro.hpp>
#include <chrono>
#include <thread>
TEST_CASE("mutex single waiter not locked exclusive", "[shared_mutex]")
{
auto tp = std::make_shared<coro::thread_pool>(coro::thread_pool::options{.thread_count = 1});
std::vector<uint64_t> output;
coro::shared_mutex<coro::thread_pool> m{tp};
auto make_emplace_task = [&](coro::shared_mutex<coro::thread_pool>& m) -> coro::task<void> {
std::cerr << "Acquiring lock exclusive\n";
{
auto scoped_lock = co_await m.lock();
REQUIRE_FALSE(m.try_lock());
REQUIRE_FALSE(m.try_lock_shared());
std::cerr << "lock acquired, emplacing back 1\n";
output.emplace_back(1);
std::cerr << "coroutine done\n";
}
// The scoped lock should release the lock upon destructing.
REQUIRE(m.try_lock());
m.unlock();
co_return;
};
coro::sync_wait(make_emplace_task(m));
REQUIRE(m.try_lock());
m.unlock();
REQUIRE(output.size() == 1);
REQUIRE(output[0] == 1);
}
TEST_CASE("mutex single waiter not locked shared", "[shared_mutex]")
{
auto tp = std::make_shared<coro::thread_pool>(coro::thread_pool::options{.thread_count = 1});
std::vector<uint64_t> values{1, 2, 3};
coro::shared_mutex<coro::thread_pool> m{tp};
auto make_emplace_task = [&](coro::shared_mutex<coro::thread_pool>& m) -> coro::task<void> {
std::cerr << "Acquiring lock shared\n";
{
auto scoped_lock = co_await m.lock_shared();
REQUIRE_FALSE(m.try_lock());
REQUIRE(m.try_lock_shared());
std::cerr << "lock acquired, reading values\n";
for (const auto& v : values)
{
std::cerr << v << ",";
}
std::cerr << "\ncoroutine done\n";
m.unlock_shared(); // manually locked shared on a shared, unlock
}
// The scoped lock should release the lock upon destructing.
REQUIRE(m.try_lock());
m.unlock();
co_return;
};
coro::sync_wait(make_emplace_task(m));
REQUIRE(m.try_lock_shared());
m.unlock_shared();
REQUIRE(m.try_lock());
m.unlock();
}
TEST_CASE("mutex many shared and exclusive waiters interleaved", "[shared_mutex]")
{
auto tp = std::make_shared<coro::io_scheduler>(
coro::io_scheduler::options{.pool = coro::thread_pool::options{.thread_count = 8}});
coro::shared_mutex<coro::io_scheduler> m{tp};
std::atomic<bool> read_value{false};
auto make_shared_task = [&]() -> coro::task<bool> {
co_await tp->schedule();
std::cerr << "make_shared_task shared lock acquiring\n";
auto scoped_lock = co_await m.lock_shared();
std::cerr << "make_shared_task shared lock acquired\n";
bool value = read_value.load(std::memory_order::acquire);
std::cerr << "make_shared_task shared lock releasing on thread_id = " << std::this_thread::get_id() << "\n";
co_return value;
};
auto make_exclusive_task = [&]() -> coro::task<void> {
// Let some readers get through.
co_await tp->yield_for(std::chrono::milliseconds{50});
{
std::cerr << "make_shared_task exclusive lock acquiring\n";
auto scoped_lock = co_await m.lock();
std::cerr << "make_shared_task exclusive lock acquired\n";
// Stack readers on the mutex
co_await tp->yield_for(std::chrono::milliseconds{50});
read_value.exchange(true, std::memory_order::release);
std::cerr << "make_shared_task exclusive lock releasing\n";
}
co_return;
};
auto make_shared_tasks_task = [&]() -> coro::task<void> {
co_await tp->schedule();
std::vector<coro::task<bool>> shared_tasks{};
bool stop{false};
while (!stop)
{
shared_tasks.emplace_back(make_shared_task());
shared_tasks.back().resume();
co_await tp->yield_for(std::chrono::milliseconds{1});
for (const auto& st : shared_tasks)
{
if (st.is_ready())
{
stop = st.promise().return_value();
}
}
}
while (true)
{
bool tasks_remaining{false};
for (const auto& st : shared_tasks)
{
if (!st.is_ready())
{
tasks_remaining = true;
break;
}
}
if (!tasks_remaining)
{
break;
}
}
co_return;
};
coro::sync_wait(coro::when_all(make_shared_tasks_task(), make_exclusive_task()));
}