From 9ff1e82e7dbdf31ddf1c534853da4581a1f41bd5 Mon Sep 17 00:00:00 2001 From: l0rinc Date: Tue, 17 Feb 2026 16:21:30 -0500 Subject: [PATCH 01/11] test: cleanup, block threads via semaphore instead of shared_future No-behavior change. --- src/test/threadpool_tests.cpp | 60 ++++++++++++++--------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/src/test/threadpool_tests.cpp b/src/test/threadpool_tests.cpp index 850cd16650d..aee9a722ae8 100644 --- a/src/test/threadpool_tests.cpp +++ b/src/test/threadpool_tests.cpp @@ -10,6 +10,8 @@ #include #include +#include +#include // General test values int NUM_WORKERS_DEFAULT = 0; @@ -51,28 +53,18 @@ template return std::move(*Assert(pool.Submit(std::forward(fn)))); } -// Block a number of worker threads by submitting tasks that wait on `blocker_future`. +// Block a number of worker threads by submitting tasks that wait on `release_sem`. // Returns the futures of the blocking tasks, ensuring all have started and are waiting. -std::vector> BlockWorkers(ThreadPool& threadPool, const std::shared_future& blocker_future, int num_of_threads_to_block) +std::vector> BlockWorkers(ThreadPool& threadPool, std::counting_semaphore<>& release_sem, size_t num_of_threads_to_block) { - // Per-thread ready promises to ensure all workers are actually blocked - std::vector> ready_promises(num_of_threads_to_block); - std::vector> ready_futures; - ready_futures.reserve(num_of_threads_to_block); - for (auto& p : ready_promises) ready_futures.emplace_back(p.get_future()); - - // Fill all workers with blocking tasks - std::vector> blocking_tasks; - for (int i = 0; i < num_of_threads_to_block; i++) { - std::promise& ready = ready_promises[i]; - blocking_tasks.emplace_back(Submit(threadPool, [blocker_future, &ready]() { - ready.set_value(); - blocker_future.wait(); - })); - } - - // Wait until all threads are actually blocked - WAIT_FOR(ready_futures); + assert(threadPool.WorkersCount() >= num_of_threads_to_block); + std::latch ready{static_cast(num_of_threads_to_block)}; + std::vector> blocking_tasks(num_of_threads_to_block); + for (auto& f : blocking_tasks) f = Submit(threadPool, [&] { + ready.count_down(); + release_sem.acquire(); + }); + ready.wait(); return blocking_tasks; } @@ -115,10 +107,8 @@ BOOST_AUTO_TEST_CASE(single_available_worker_executes_all_tasks) { ThreadPool threadPool(POOL_NAME); threadPool.Start(NUM_WORKERS_DEFAULT); - // Single blocking future for all threads - std::promise blocker; - std::shared_future blocker_future(blocker.get_future()); - const auto blocking_tasks = BlockWorkers(threadPool, blocker_future, NUM_WORKERS_DEFAULT - 1); + std::counting_semaphore<> blocker(0); + const auto blocking_tasks = BlockWorkers(threadPool, blocker, NUM_WORKERS_DEFAULT - 1); // Now execute tasks on the single available worker // and check that all the tasks are executed. @@ -132,7 +122,7 @@ BOOST_AUTO_TEST_CASE(single_available_worker_executes_all_tasks) WAIT_FOR(futures); BOOST_CHECK_EQUAL(counter, num_tasks); - blocker.set_value(); + blocker.release(NUM_WORKERS_DEFAULT - 1); WAIT_FOR(blocking_tasks); threadPool.Stop(); BOOST_CHECK_EQUAL(threadPool.WorkersCount(), 0); @@ -195,9 +185,8 @@ BOOST_AUTO_TEST_CASE(process_tasks_manually_when_workers_busy) ThreadPool threadPool(POOL_NAME); threadPool.Start(NUM_WORKERS_DEFAULT); - std::promise blocker; - std::shared_future blocker_future(blocker.get_future()); - const auto& blocking_tasks = BlockWorkers(threadPool, blocker_future, NUM_WORKERS_DEFAULT); + std::counting_semaphore<> blocker(0); + const auto& blocking_tasks = BlockWorkers(threadPool, blocker, NUM_WORKERS_DEFAULT); // Now submit tasks and check that none of them are executed. int num_tasks = 20; @@ -216,7 +205,7 @@ BOOST_AUTO_TEST_CASE(process_tasks_manually_when_workers_busy) } BOOST_CHECK_EQUAL(counter.load(), num_tasks); BOOST_CHECK_EQUAL(threadPool.WorkQueueSize(), 0); - blocker.set_value(); + blocker.release(NUM_WORKERS_DEFAULT); threadPool.Stop(); WAIT_FOR(blocking_tasks); } @@ -244,9 +233,8 @@ BOOST_AUTO_TEST_CASE(task_submitted_while_busy_completes) ThreadPool threadPool(POOL_NAME); threadPool.Start(NUM_WORKERS_DEFAULT); - std::promise blocker; - std::shared_future blocker_future(blocker.get_future()); - const auto& blocking_tasks = BlockWorkers(threadPool, blocker_future, NUM_WORKERS_DEFAULT); + std::counting_semaphore<> blocker(0); + const auto& blocking_tasks = BlockWorkers(threadPool, blocker, NUM_WORKERS_DEFAULT); // Submit an extra task that should execute once a worker is free std::future future = Submit(threadPool, []() { return true; }); @@ -257,7 +245,7 @@ BOOST_AUTO_TEST_CASE(task_submitted_while_busy_completes) // Wait a short moment before unblocking the threads to mimic a concurrent shutdown std::thread thread_unblocker([&blocker]() { UninterruptibleSleep(300ms); - blocker.set_value(); + blocker.release(NUM_WORKERS_DEFAULT); }); // Stop the pool while the workers are still blocked @@ -314,13 +302,13 @@ BOOST_AUTO_TEST_CASE(interrupt_blocks_new_submissions) // One worker is blocked, another calls Interrupt(), and the remaining one waits for tasks. threadPool.Start(/*num_workers=*/3); std::atomic counter{0}; - std::promise blocker; - const auto blocking_tasks = BlockWorkers(threadPool, blocker.get_future().share(), 1); + std::counting_semaphore<> blocker(0); + const auto blocking_tasks = BlockWorkers(threadPool, blocker, 1); Submit(threadPool, [&threadPool, &counter]{ threadPool.Interrupt(); counter.fetch_add(1, std::memory_order_relaxed); }).get(); - blocker.set_value(); // unblock worker + blocker.release(1); // unblock worker BOOST_CHECK_EQUAL(counter.load(), 1); threadPool.Stop(); From 8cd4a4363fb85f5487a19ace82aa0d12d5fab450 Mon Sep 17 00:00:00 2001 From: furszy Date: Thu, 19 Feb 2026 17:40:46 -0500 Subject: [PATCH 02/11] threadpool: guard against Start-Stop race Stop() has two windows where Start() could cause troubles: 1) m_workers is temporarily empty while workers are being joined, this creates a window where Start() could slip through and reset m_interrupt to false, preventing the old workers from exiting and causing a deadlock. 2) Start() could be called after workers are joined but before the empty() sanity check on m_work_queue, causing a crash. Fix both races by keeping m_interrupt set for the entire duration of Stop(), so any concurrent Start() call is rejected until all workers have exited. Co-authored-by: Hodlinator <172445034+hodlinator@users.noreply.github.com> --- src/util/threadpool.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/util/threadpool.h b/src/util/threadpool.h index c039b59c4de..6fc294989d1 100644 --- a/src/util/threadpool.h +++ b/src/util/threadpool.h @@ -105,8 +105,8 @@ public: { assert(num_workers > 0); LOCK(m_mutex); + if (m_interrupt) throw std::runtime_error("Thread pool has been interrupted or is stopping"); if (!m_workers.empty()) throw std::runtime_error("Thread pool already started"); - m_interrupt = false; // Reset // Create workers m_workers.reserve(num_workers); @@ -122,6 +122,7 @@ public: * Any remaining tasks in the queue will be processed before returning. * * Must be called from a controller (non-worker) thread. + * Concurrent calls to Start() will be rejected while Stop() is in progress. */ void Stop() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { @@ -139,9 +140,12 @@ public: } m_cv.notify_all(); for (auto& worker : threads_to_join) worker.join(); + // Since we currently wait for tasks completion, sanity-check empty queue - WITH_LOCK(m_mutex, Assume(m_work_queue.empty())); - // Note: m_interrupt is left true until next Start() + LOCK(m_mutex); + Assume(m_work_queue.empty()); + // Re-allow Start() now that all workers have exited + m_interrupt = false; } enum class SubmitError { @@ -202,6 +206,10 @@ public: * * Wakes all worker threads so they can drain the queue and exit. * Unlike Stop(), this function does not wait for threads to finish. + * + * Note: The next step in the pool lifecycle is calling Stop(), which + * releases any dangling resources and resets the pool state + * for shutdown or restart. */ void Interrupt() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { From e88d2744301a434064714f0a21e1395d41ac3984 Mon Sep 17 00:00:00 2001 From: furszy Date: Thu, 19 Feb 2026 17:32:37 -0500 Subject: [PATCH 03/11] test: add threadpool Start-Stop race coverage Co-authored-by: Hodlinator <172445034+hodlinator@users.noreply.github.com> --- src/test/threadpool_tests.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/threadpool_tests.cpp b/src/test/threadpool_tests.cpp index aee9a722ae8..d956c20544a 100644 --- a/src/test/threadpool_tests.cpp +++ b/src/test/threadpool_tests.cpp @@ -37,6 +37,7 @@ struct ThreadPoolFixture { // 8) Submit task when all threads are busy, stop pool and verify task gets executed. // 9) Congestion test; create more workers than available cores. // 10) Ensure Interrupt() prevents further submissions. +// 11) Start() must not cause a deadlock when called during Stop(). BOOST_FIXTURE_TEST_SUITE(threadpool_tests, ThreadPoolFixture) #define WAIT_FOR(futures) \ @@ -316,4 +317,38 @@ BOOST_AUTO_TEST_CASE(interrupt_blocks_new_submissions) BOOST_CHECK_EQUAL(threadPool.WorkersCount(), 0); } +// Test 11, Start() must not cause a deadlock when called during Stop() +BOOST_AUTO_TEST_CASE(start_mid_stop_does_not_deadlock) +{ + ThreadPool threadPool(POOL_NAME); + threadPool.Start(NUM_WORKERS_DEFAULT); + + // Keep all workers busy so Stop() gets stuck waiting for them to finish during join() + std::counting_semaphore<> workers_blocker(0); + const auto blocking_tasks = BlockWorkers(threadPool, workers_blocker, NUM_WORKERS_DEFAULT); + + std::thread stopper_thread([&threadPool] { threadPool.Stop(); }); + + // Stop() takes ownership of the workers before joining them, so WorkersCount() + // hits 0 the moment Stop() is waiting for them to join. That is our signal + // to call Start() right into the middle of the joining phase. + while (threadPool.WorkersCount() != 0) { + std::this_thread::yield(); // let the OS breathe so it can switch context + } + // Now we know for sure the stopper thread is hanging while workers are still alive. + // Restart the pool and resume workers so the stopper thread can proceed. + // This will throw an exception only if the pool handles Start-Stop race properly, + // otherwise it will proceed and hang the stopper_thread. + try { + threadPool.Start(NUM_WORKERS_DEFAULT); + } catch (std::exception& e) { + BOOST_CHECK_EQUAL(e.what(), "Thread pool has been interrupted or is stopping"); + } + workers_blocker.release(NUM_WORKERS_DEFAULT); + WAIT_FOR(blocking_tasks); + + // If Stop() is stuck, joining the stopper thread will deadlock + stopper_thread.join(); +} + BOOST_AUTO_TEST_SUITE_END() From bf2c607aaa22d253b9367c11b0a198bd4244ad2f Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 10 Feb 2026 16:32:47 -0500 Subject: [PATCH 04/11] threadpool: active-wait during shutdown Instead of waiting for the workers to finish processing tasks, help them actively inside Stop(). This speeds up the JSON-RPC and REST server shutdown procedure, and results in a faster node shutdown when many requests remain unhandled --- src/util/threadpool.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/util/threadpool.h b/src/util/threadpool.h index 6fc294989d1..ca39afd4ab2 100644 --- a/src/util/threadpool.h +++ b/src/util/threadpool.h @@ -139,6 +139,9 @@ public: threads_to_join.swap(m_workers); } m_cv.notify_all(); + // Help draining queue + while (ProcessTask()) {} + // Free resources for (auto& worker : threads_to_join) worker.join(); // Since we currently wait for tasks completion, sanity-check empty queue @@ -187,18 +190,19 @@ public: * @brief Execute a single queued task synchronously. * Removes one task from the queue and executes it on the calling thread. */ - void ProcessTask() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) + bool ProcessTask() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { std::packaged_task task; { LOCK(m_mutex); - if (m_work_queue.empty()) return; + if (m_work_queue.empty()) return false; // Pop the task task = std::move(m_work_queue.front()); m_work_queue.pop(); } task(); + return true; } /** From ca101a2315774f0ed65da633ba99899fd0dad740 Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 17 Feb 2026 16:45:20 -0500 Subject: [PATCH 05/11] test: coverage for queued tasks completion after interrupt --- src/test/threadpool_tests.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/test/threadpool_tests.cpp b/src/test/threadpool_tests.cpp index d956c20544a..4855b3343e5 100644 --- a/src/test/threadpool_tests.cpp +++ b/src/test/threadpool_tests.cpp @@ -38,6 +38,7 @@ struct ThreadPoolFixture { // 9) Congestion test; create more workers than available cores. // 10) Ensure Interrupt() prevents further submissions. // 11) Start() must not cause a deadlock when called during Stop(). +// 12) Ensure queued tasks complete after Interrupt(). BOOST_FIXTURE_TEST_SUITE(threadpool_tests, ThreadPoolFixture) #define WAIT_FOR(futures) \ @@ -351,4 +352,32 @@ BOOST_AUTO_TEST_CASE(start_mid_stop_does_not_deadlock) stopper_thread.join(); } +// Test 12, queued tasks complete after Interrupt() +BOOST_AUTO_TEST_CASE(queued_tasks_complete_after_interrupt) +{ + ThreadPool threadPool(POOL_NAME); + threadPool.Start(NUM_WORKERS_DEFAULT); + + std::counting_semaphore<> blocker(0); + const auto blocking_tasks = BlockWorkers(threadPool, blocker, NUM_WORKERS_DEFAULT); + + // Queue tasks while all workers are busy, then interrupt + std::atomic counter{0}; + const int num_tasks = 10; + std::vector> futures; + futures.reserve(num_tasks); + for (int i = 0; i < num_tasks; i++) { + futures.emplace_back(Submit(threadPool, [&counter]{ counter.fetch_add(1, std::memory_order_relaxed); })); + } + threadPool.Interrupt(); + + // Queued tasks must still complete despite the interrupt + blocker.release(NUM_WORKERS_DEFAULT); + WAIT_FOR(futures); + BOOST_CHECK_EQUAL(counter.load(), num_tasks); + + threadPool.Stop(); + WAIT_FOR(blocking_tasks); +} + BOOST_AUTO_TEST_SUITE_END() From 3b7cbcafcb9b318bf1fa00a3499f514c5ebe9bb6 Mon Sep 17 00:00:00 2001 From: seduless Date: Thu, 19 Feb 2026 20:49:58 -0500 Subject: [PATCH 06/11] test: ensure Stop() thread helps drain the queue Exercise the case where tasks remain pending and verify that the thread calling Stop() participates in draining the queue --- src/test/threadpool_tests.cpp | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/threadpool_tests.cpp b/src/test/threadpool_tests.cpp index 4855b3343e5..c7878c3523b 100644 --- a/src/test/threadpool_tests.cpp +++ b/src/test/threadpool_tests.cpp @@ -39,6 +39,7 @@ struct ThreadPoolFixture { // 10) Ensure Interrupt() prevents further submissions. // 11) Start() must not cause a deadlock when called during Stop(). // 12) Ensure queued tasks complete after Interrupt(). +// 13) Ensure the Stop() calling thread helps drain the queue. BOOST_FIXTURE_TEST_SUITE(threadpool_tests, ThreadPoolFixture) #define WAIT_FOR(futures) \ @@ -380,4 +381,40 @@ BOOST_AUTO_TEST_CASE(queued_tasks_complete_after_interrupt) WAIT_FOR(blocking_tasks); } +// Test 13, ensure the Stop() calling thread helps drain the queue +BOOST_AUTO_TEST_CASE(stop_active_wait_drains_queue) +{ + ThreadPool threadPool(POOL_NAME); + threadPool.Start(NUM_WORKERS_DEFAULT); + + std::counting_semaphore<> blocker(0); + const auto blocking_tasks = BlockWorkers(threadPool, blocker, NUM_WORKERS_DEFAULT); + + auto main_thread_id = std::this_thread::get_id(); + std::atomic main_thread_tasks{0}; + const size_t num_tasks = 20; + for (size_t i = 0; i < num_tasks; i++) { + (void)Submit(threadPool, [&main_thread_tasks, main_thread_id]() { + if (std::this_thread::get_id() == main_thread_id) + main_thread_tasks.fetch_add(1, std::memory_order_relaxed); + }); + } + BOOST_CHECK_EQUAL(threadPool.WorkQueueSize(), num_tasks); + + // Delay release so Stop() drain all tasks from the calling thread + std::thread unblocker([&blocker, &threadPool]() { + while (threadPool.WorkQueueSize() > 0) { + std::this_thread::yield(); + } + blocker.release(NUM_WORKERS_DEFAULT); + }); + + threadPool.Stop(); + unblocker.join(); + + // Check the main thread processed all tasks + BOOST_CHECK_EQUAL(main_thread_tasks.load(), num_tasks); + WAIT_FOR(blocking_tasks); +} + BOOST_AUTO_TEST_SUITE_END() From dbbb780af02d850a1f9257f18610cfb9de9cb828 Mon Sep 17 00:00:00 2001 From: Hodlinator <172445034+hodlinator@users.noreply.github.com> Date: Tue, 24 Feb 2026 13:56:17 -0300 Subject: [PATCH 07/11] test: move and simplify BOOST_CHECK ostream helpers Move the operator<< overloads used by BOOST_CHECK_* out of the unit test machinery test/setup_common, into test/util/common.h. And replace the individual per-type ToString() overloads with a single concept-constrained template that covers any type exposing a ToString() method. This is important to not add uint256.h and transaction_identifier.h dependencies to the shared test/util/common.h file. Co-authored-by: furszy --- src/test/arith_uint256_tests.cpp | 2 +- src/test/blockencodings_tests.cpp | 1 + src/test/blockfilter_index_tests.cpp | 1 + src/test/blockfilter_tests.cpp | 2 +- src/test/blockmanager_tests.cpp | 1 + src/test/bloom_tests.cpp | 1 + src/test/coins_tests.cpp | 1 + src/test/crypto_tests.cpp | 1 + src/test/dbwrapper_tests.cpp | 1 + src/test/getarg_tests.cpp | 1 + src/test/headers_sync_chainwork_tests.cpp | 1 + src/test/interfaces_tests.cpp | 1 + src/test/key_tests.cpp | 1 + src/test/logging_tests.cpp | 1 + src/test/merkle_tests.cpp | 1 + src/test/merkleblock_tests.cpp | 1 + src/test/miner_tests.cpp | 1 + src/test/minisketch_tests.cpp | 1 + src/test/multisig_tests.cpp | 1 + src/test/netbase_tests.cpp | 1 + src/test/node_init_tests.cpp | 1 + src/test/orphanage_tests.cpp | 1 + src/test/pcp_tests.cpp | 1 + src/test/pow_tests.cpp | 1 + src/test/rest_tests.cpp | 1 + src/test/rpc_tests.cpp | 1 + src/test/script_standard_tests.cpp | 1 + src/test/script_tests.cpp | 2 +- src/test/serialize_tests.cpp | 1 + src/test/sighash_tests.cpp | 1 + src/test/streams_tests.cpp | 1 + src/test/transaction_tests.cpp | 1 + src/test/txdownload_tests.cpp | 1 + src/test/txospenderindex_tests.cpp | 1 + src/test/txpackage_tests.cpp | 1 + src/test/txreconciliation_tests.cpp | 1 + src/test/txvalidation_tests.cpp | 1 + src/test/uint256_tests.cpp | 2 +- src/test/util/common.h | 39 +++++++++++++++++++ src/test/util/setup_common.cpp | 23 ----------- src/test/util/setup_common.h | 23 ----------- src/test/util_string_tests.cpp | 1 + src/test/util_tests.cpp | 1 + src/test/validation_block_tests.cpp | 1 + src/test/validation_chainstate_tests.cpp | 1 + .../validation_chainstatemanager_tests.cpp | 1 + src/test/validation_flush_tests.cpp | 1 + src/test/versionbits_tests.cpp | 1 + src/wallet/test/coinselector_tests.cpp | 1 + src/wallet/test/db_tests.cpp | 2 +- src/wallet/test/wallet_tests.cpp | 1 + src/wallet/test/wallet_transaction_tests.cpp | 1 + src/wallet/test/walletload_tests.cpp | 1 + 53 files changed, 89 insertions(+), 51 deletions(-) create mode 100644 src/test/util/common.h diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp index 0a15458041e..016b4658719 100644 --- a/src/test/arith_uint256_tests.cpp +++ b/src/test/arith_uint256_tests.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include +#include #include #include diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 0fbe0f3c134..e4200cace25 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index d7d10dfb1ae..25762e070db 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp index c8334dabe10..0fc7d5b29ee 100644 --- a/src/test/blockfilter_tests.cpp +++ b/src/test/blockfilter_tests.cpp @@ -3,7 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include +#include #include #include diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index f1b6c017ad8..68178dec3eb 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 8e02cfd08c6..ed333c34c0d 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 0b7af376eff..8321bf6a18c 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 5588d4cdbc6..b348793bfb6 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index d3a9e54348b..3896ea64da5 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index ec17fe39971..d349ceea44b 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/src/test/headers_sync_chainwork_tests.cpp b/src/test/headers_sync_chainwork_tests.cpp index f9426fa3114..bba612f8b46 100644 --- a/src/test/headers_sync_chainwork_tests.cpp +++ b/src/test/headers_sync_chainwork_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp index da0f5eeccef..1a98256ce2e 100644 --- a/src/test/interfaces_tests.cpp +++ b/src/test/interfaces_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include