/* Copyright (c) 2016, 2019-2021, Arvid Norberg Copyright (c) 2016, Steven Siloti Copyright (c) 2020-2021, Alden Torres All rights reserved. You may use, distribute and modify this code under the terms of the BSD license, see LICENSE file. */ #include "test.hpp" #include "simulator/simulator.hpp" #include "libtorrent/aux_/disk_io_thread_pool.hpp" #include "libtorrent/aux_/disk_job.hpp" #include "libtorrent/io_context.hpp" #include #include #include using lt::io_context; using namespace std::chrono_literals; std::mutex g_job_mutex; void thread_fun(lt::aux::disk_io_thread_pool& pool, lt::executor_work_guard) { std::unique_lock l(g_job_mutex); for (;;) { auto const result = pool.wait_for_job(l); if (result == lt::aux::wait_result::exit_thread) break; lt::aux::disk_job* j = static_cast(pool.pop_front()); l.unlock(); // pretend to perform job TORRENT_UNUSED(j); std::this_thread::sleep_for(1ms); l.lock(); } } /* TORRENT_TEST(disk_io_thread_pool_idle_reaping) { sim::default_config cfg; sim::simulation sim{ cfg }; test_threads threads; sim::asio::io_context ios(sim); lt::aux::disk_io_thread_pool pool(threads, ios); threads.m_pool = &pool; pool.set_max_threads(3); pool.job_queued(3); TEST_EQUAL(pool.num_threads(), 3); // make sure all the threads are up and settled in the active state threads.set_active_threads(3); // first just kill one thread threads.set_active_threads(2); lt::aux::deadline_timer idle_delay(ios); // the thread will be killed the second time the reaper runs and we need // to wait one extra minute to make sure the check runs after the reaper idle_delay.expires_after(std::chrono::minutes(3)); idle_delay.async_wait([&](lt::error_code const&) { // this is a kludge to work around a race between the thread // exiting and checking the number of threads // in production we only check num_threads from the disk I/O threads // so there are no race problems there threads.wait_for_thread_exit(2); TEST_EQUAL(pool.num_threads(), 2); sim.stop(); }); sim.run(); sim.restart(); // now kill the rest threads.set_active_threads(0); idle_delay.expires_after(std::chrono::minutes(3)); idle_delay.async_wait([&](lt::error_code const&) { // see comment above about this kludge threads.wait_for_thread_exit(0); TEST_EQUAL(pool.num_threads(), 0); }); sim.run(); } */ TORRENT_TEST(disk_io_thread_pool_abort_wait) { sim::default_config cfg; sim::simulation sim{ cfg }; sim::asio::io_context ios(sim); lt::aux::disk_io_thread_pool pool(&thread_fun, ios); pool.set_max_threads(3); lt::aux::disk_job jobs[3]; { std::unique_lock l(g_job_mutex); for (auto& j : jobs) pool.push_back(&j); pool.submit_jobs(); } TEST_EQUAL(pool.num_threads(), 3); pool.abort(true); TEST_EQUAL(pool.num_threads(), 0); } #if 0 // disabled for now because io_context::work doesn't work under the simulator // and we need it to stop this test from exiting prematurely TORRENT_TEST(disk_io_thread_pool_abort_no_wait) { sim::default_config cfg; sim::simulation sim{ cfg }; sim::asio::io_context ios(sim); lt::aux::disk_io_thread_pool pool(&thread_fun, ios); pool.set_max_threads(3); pool.job_queued(3); TEST_EQUAL(pool.num_threads(), 3); pool.abort(false); TEST_EQUAL(pool.num_threads(), 0); sim.run(); } #endif TORRENT_TEST(disk_io_thread_pool_max_threads) { sim::default_config cfg; sim::simulation sim{ cfg }; sim::asio::io_context ios(sim); lt::aux::disk_io_thread_pool pool(thread_fun, ios); // first check that the thread limit is respected when adding jobs pool.set_max_threads(3); lt::aux::disk_job jobs[4]; { std::unique_lock l(g_job_mutex); for (auto& j : jobs) pool.push_back(&j); pool.submit_jobs(); } TEST_EQUAL(pool.num_threads(), 3); // now check that the number of threads is reduced when the max threads is reduced pool.set_max_threads(2); std::this_thread::sleep_for(20ms); int const num_threads = pool.num_threads(); TEST_EQUAL(num_threads, 2); }