From f242967f6e4a775ef54d68ac802011b7b638ad6a Mon Sep 17 00:00:00 2001 From: Navia Bheeman Date: Thu, 1 Jun 2023 19:12:35 +0530 Subject: [PATCH 01/20] remove boost::chrono --- build-aux/m4/ax_boost_chrono.m4 | 118 -------------------------------- configure.ac | 53 +------------- src/addrman.cpp | 4 +- src/scheduler.cpp | 52 +++++--------- src/scheduler.h | 16 +++-- src/test/scheduler_tests.cpp | 22 +++--- src/utiltime.cpp | 3 +- 7 files changed, 44 insertions(+), 224 deletions(-) delete mode 100644 build-aux/m4/ax_boost_chrono.m4 diff --git a/build-aux/m4/ax_boost_chrono.m4 b/build-aux/m4/ax_boost_chrono.m4 deleted file mode 100644 index da6478f05a..0000000000 --- a/build-aux/m4/ax_boost_chrono.m4 +++ /dev/null @@ -1,118 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_boost_chrono.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_CHRONO -# -# DESCRIPTION -# -# Test for Chrono library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# . -# -# This macro calls: -# -# AC_SUBST(BOOST_CHRONO_LIB) -# -# And sets: -# -# HAVE_BOOST_CHRONO -# -# LICENSE -# -# Copyright (c) 2012 Xiyue Deng -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 4 - -AC_DEFUN([AX_BOOST_CHRONO], -[ - AC_ARG_WITH([boost-chrono], - AS_HELP_STRING([--with-boost-chrono@<:@=special-lib@:>@], - [use the Chrono library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-chrono=boost_chrono-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_chrono_lib="" - else - want_boost="yes" - ax_boost_user_chrono_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::Chrono library is available, - ax_cv_boost_chrono, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], - [[boost::chrono::system_clock::time_point* time = new boost::chrono::system_clock::time_point; delete time;]])], - ax_cv_boost_chrono=yes, ax_cv_boost_chrono=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_chrono" = "xyes"; then - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_CHRONO,,[define if the Boost::Chrono library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - if test "x$ax_boost_user_chrono_lib" = "x"; then - for libextension in `ls $BOOSTLIBDIR/libboost_chrono*.so* $BOOSTLIBDIR/libboost_chrono*.dylib* $BOOSTLIBDIR/libboost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_chrono.*\)\.so.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.a.*$;\1;'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], - [link_chrono="no"]) - done - if test "x$link_chrono" != "xyes"; then - for libextension in `ls $BOOSTLIBDIR/boost_chrono*.dll* $BOOSTLIBDIR/boost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_chrono.*\)\.dll.*$;\1;' -e 's;^\(boost_chrono.*\)\.a.*$;\1;'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], - [link_chrono="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_chrono_lib boost_chrono-$ax_boost_user_chrono_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], - [link_chrono="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the library!) - fi - if test "x$link_chrono" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/configure.ac b/configure.ac index e877b511ee..bcf5d94c17 100644 --- a/configure.ac +++ b/configure.ac @@ -912,7 +912,6 @@ fi AX_BOOST_SYSTEM AX_BOOST_FILESYSTEM AX_BOOST_THREAD -AX_BOOST_CHRONO dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic dnl counter implementations. In 1.63 and later the std::atomic approach is default. @@ -960,7 +959,7 @@ fi if test x$use_boost = xyes; then -BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" +BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB " dnl If boost (prior to 1.57) was built without c++11, it emulated scoped enums @@ -998,56 +997,6 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" -dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however -dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if -dnl a working version is available, else fall back to sleep. sleep was removed -dnl after 1.56. -dnl If neither is available, abort. -TEMP_LIBS="$LIBS" -LIBS="$BOOST_LIBS $LIBS" -TEMP_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" -AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - #if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) - boost::this_thread::sleep_for(boost::chrono::milliseconds(0)); - #else - choke me - #endif - ]])], - [boost_sleep=yes; - AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])], - [boost_sleep=no]) -LIBS="$TEMP_LIBS" -CPPFLAGS="$TEMP_CPPFLAGS" - -if test x$boost_sleep != xyes; then -TEMP_LIBS="$LIBS" -LIBS="$BOOST_LIBS $LIBS" -TEMP_CPPFLAGS="$CPPFLAGS" -CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" -AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include - #include - #include - ]],[[ - #if BOOST_VERSION <= 105600 - boost::this_thread::sleep(boost::posix_time::milliseconds(0)); - #else - choke me - #endif - ]])], - [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])], - [boost_sleep=no]) -LIBS="$TEMP_LIBS" -CPPFLAGS="$TEMP_CPPFLAGS" -fi - -if test x$boost_sleep != xyes; then - AC_MSG_ERROR(No working boost sleep implementation found.) -fi fi diff --git a/src/addrman.cpp b/src/addrman.cpp index 093b263ab3..1bfcb1ee55 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -9,6 +9,8 @@ #include #include +#include + int CAddrInfo::GetTriedBucket(const uint256& nKey) const { uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetCheapHash(); @@ -60,7 +62,7 @@ double CAddrInfo::GetChance(int64_t nNow) const fChance *= 0.01; // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages. - fChance *= pow(0.66, std::min(nAttempts, 8)); + fChance *= std::pow(0.66, std::min(nAttempts, 8)); return fChance; } diff --git a/src/scheduler.cpp b/src/scheduler.cpp index d2b882d842..2d76d1186d 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -9,6 +9,10 @@ #include #include +#include + +#include +#include CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false) { @@ -19,19 +23,9 @@ CScheduler::~CScheduler() assert(nThreadsServicingQueue == 0); } - -#if BOOST_VERSION < 105000 -static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t) -{ - // Creating the posix_time using from_time_t loses sub-second precision. So rather than exporting the time_point to time_t, - // start with a posix_time at the epoch (0) and add the milliseconds that have passed since then. - return boost::posix_time::from_time_t(0) + boost::posix_time::milliseconds(boost::chrono::duration_cast(t.time_since_epoch()).count()); -} -#endif - void CScheduler::serviceQueue() { - boost::unique_lock lock(newTaskMutex); + WaitableLock lock(newTaskMutex); ++nThreadsServicingQueue; // newTaskMutex is locked throughout this loop EXCEPT @@ -40,7 +34,7 @@ void CScheduler::serviceQueue() while (!shouldStop()) { try { if (!shouldStop() && taskQueue.empty()) { - reverse_lock > rlock(lock); + reverse_lock rlock(lock); } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. @@ -49,22 +43,12 @@ void CScheduler::serviceQueue() // Wait until either there is a new task, or until // the time of the first item on the queue: - -// wait_until needs boost 1.50 or later; older versions have timed_wait: -#if BOOST_VERSION < 105000 - while (!shouldStop() && !taskQueue.empty() && - newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) { - // Keep waiting until timeout - } -#else - // Some boost versions have a conflicting overload of wait_until that returns void. - // Explicitly use a template here to avoid hitting that overload. while (!shouldStop() && !taskQueue.empty()) { - boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first; - if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout) + std::chrono::steady_clock::time_point timeToWaitFor = taskQueue.begin()->first; + if (newTaskScheduled.wait_until(lock, timeToWaitFor) == std::cv_status::timeout) break; // Exit loop after timeout, it means we reached the time of the event } -#endif + // If there are multiple threads, the queue can empty while we're waiting (another // thread may service the task we were waiting on). if (shouldStop() || taskQueue.empty()) @@ -76,7 +60,7 @@ void CScheduler::serviceQueue() { // Unlock before calling f, so it can reschedule itself or another task // without deadlocking: - reverse_lock > rlock(lock); + reverse_lock rlock(lock); f(); } } catch (...) { @@ -91,7 +75,7 @@ void CScheduler::serviceQueue() void CScheduler::stop(bool drain) { { - boost::unique_lock lock(newTaskMutex); + WaitableLock lock(newTaskMutex); if (drain) stopWhenEmpty = true; else @@ -100,10 +84,10 @@ void CScheduler::stop(bool drain) newTaskScheduled.notify_all(); } -void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t) +void CScheduler::schedule(CScheduler::Function f, std::chrono::steady_clock::time_point t) { { - boost::unique_lock lock(newTaskMutex); + WaitableLock lock(newTaskMutex); taskQueue.insert(std::make_pair(t, f)); } newTaskScheduled.notify_one(); @@ -111,7 +95,7 @@ void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::t void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSeconds) { - schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds)); + schedule(f, std::chrono::steady_clock::now() + std::chrono::milliseconds(deltaMilliSeconds)); } static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds) @@ -125,10 +109,10 @@ void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds); } -size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, - boost::chrono::system_clock::time_point &last) const +size_t CScheduler::getQueueInfo(std::chrono::steady_clock::time_point &first, + std::chrono::steady_clock::time_point &last) const { - boost::unique_lock lock(newTaskMutex); + WaitableLock lock(newTaskMutex); size_t result = taskQueue.size(); if (!taskQueue.empty()) { first = taskQueue.begin()->first; @@ -138,7 +122,7 @@ size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, } bool CScheduler::AreThreadsServicingQueue() const { - boost::unique_lock lock(newTaskMutex); + WaitableLock lock(newTaskMutex); return nThreadsServicingQueue; } diff --git a/src/scheduler.h b/src/scheduler.h index a1c064ef1e..79f473bb40 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -10,9 +10,11 @@ // boost::thread / boost::chrono should be ported to std::thread / std::chrono // when we support C++11. // -#include + #include #include +#include +#include #include @@ -43,7 +45,7 @@ class CScheduler typedef std::function Function; // Call func at/after time t - void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now()); + void schedule(Function f, std::chrono::steady_clock::time_point t=std::chrono::steady_clock::now()); // Convenience method: call f once deltaSeconds from now void scheduleFromNow(Function f, int64_t deltaMilliSeconds); @@ -68,16 +70,16 @@ class CScheduler // Returns number of tasks waiting to be serviced, // and first and last task times - size_t getQueueInfo(boost::chrono::system_clock::time_point &first, - boost::chrono::system_clock::time_point &last) const; + size_t getQueueInfo(std::chrono::steady_clock::time_point &first, + std::chrono::steady_clock::time_point &last) const; // Returns true if there are threads actively running in serviceQueue() bool AreThreadsServicingQueue() const; private: - std::multimap taskQueue; - boost::condition_variable newTaskScheduled; - mutable boost::mutex newTaskMutex; + std::multimap taskQueue; + std::condition_variable newTaskScheduled; + mutable std::mutex newTaskMutex; int nThreadsServicingQueue; bool stopRequested; bool stopWhenEmpty; diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index edc43dd870..0afc08943b 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -13,13 +13,13 @@ BOOST_AUTO_TEST_SUITE(scheduler_tests) -static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, boost::chrono::system_clock::time_point rescheduleTime) +static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, std::chrono::steady_clock::time_point rescheduleTime) { { boost::unique_lock lock(mutex); counter += delta; } - boost::chrono::system_clock::time_point noTime = boost::chrono::system_clock::time_point::min(); + std::chrono::steady_clock::time_point noTime = std::chrono::steady_clock::time_point::min(); if (rescheduleTime != noTime) { CScheduler::Function f = std::bind(µTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime); s.schedule(f, rescheduleTime); @@ -28,7 +28,7 @@ static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delt static void MicroSleep(uint64_t n) { - boost::this_thread::sleep_for(boost::chrono::microseconds(n)); + std::this_thread::sleep_for(std::chrono::microseconds(n)); } BOOST_AUTO_TEST_CASE(manythreads) @@ -52,15 +52,15 @@ BOOST_AUTO_TEST_CASE(manythreads) auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000] auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000] - boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); - boost::chrono::system_clock::time_point now = start; - boost::chrono::system_clock::time_point first, last; + std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point now = start; + std::chrono::steady_clock::time_point first, last; size_t nTasks = microTasks.getQueueInfo(first, last); BOOST_CHECK(nTasks == 0); for (int i = 0; i < 100; ++i) { - boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); - boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); + std::chrono::steady_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); + std::chrono::steady_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), @@ -78,14 +78,14 @@ BOOST_AUTO_TEST_CASE(manythreads) microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); MicroSleep(600); - now = boost::chrono::system_clock::now(); + now = std::chrono::steady_clock::now(); // More threads and more tasks: for (int i = 0; i < 5; i++) microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); for (int i = 0; i < 100; i++) { - boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); - boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); + std::chrono::steady_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); + std::chrono::steady_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 694de86d9a..2bb3344618 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -61,7 +62,7 @@ int64_t GetSystemTimeInSeconds() void MilliSleep(int64_t n) { - boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); + std::this_thread::sleep_for(std::chrono::milliseconds(n)); } std::string FormatISO8601DateTime(int64_t nTime) { From 3efeff868494235d4e95c7676a31cfe20a8c7ddb Mon Sep 17 00:00:00 2001 From: Navia Bheeman Date: Thu, 1 Jun 2023 14:40:50 +0530 Subject: [PATCH 02/20] make script validation checkqueue threads use internal pool instead of global scheduler pool bitcoin#15632 --- src/bench/checkqueue.cpp | 9 +--- src/checkqueue.h | 69 ++++++++++++++++++------ src/init.cpp | 4 +- src/test/checkqueue_tests.cpp | 87 ++++++++++++------------------ src/test/test_tapyrus.cpp | 4 +- src/validation.cpp | 10 ++-- src/validation.h | 6 ++- test/lint/pending-lint-includes.sh | 1 - 8 files changed, 102 insertions(+), 88 deletions(-) diff --git a/src/bench/checkqueue.cpp b/src/bench/checkqueue.cpp index 79689f6e0b..93d5e19052 100644 --- a/src/bench/checkqueue.cpp +++ b/src/bench/checkqueue.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include @@ -37,10 +36,7 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state) void swap(PrevectorJob& x){p.swap(x.p);}; }; CCheckQueue queue {QUEUE_BATCH_SIZE}; - boost::thread_group tg; - for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) { - tg.create_thread([&]{queue.Thread();}); - } + queue.StartWorkerThreads(GetNumCores() - 1); while (state.KeepRunning()) { // Make insecure_rand here so that each iteration is identical. FastRandomContext insecure_rand(true); @@ -56,7 +52,6 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state) // it is done explicitly here for clarity control.Wait(); } - tg.interrupt_all(); - tg.join_all(); + queue.StopWorkerThreads(); } BENCHMARK(CCheckQueueSpeedPrevectorJob, 1400); diff --git a/src/checkqueue.h b/src/checkqueue.h index 978e23a7c4..e3a1f03efc 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -10,8 +10,6 @@ #include #include -#include -#include template class CCheckQueueControl; @@ -31,48 +29,51 @@ class CCheckQueue { private: //! Mutex to protect the inner state - boost::mutex mutex; + std::mutex mutex; //! Worker threads block on this when out of work - boost::condition_variable condWorker; + std::condition_variable condWorker; //! Master thread blocks on this when out of work - boost::condition_variable condMaster; + std::condition_variable condMaster; //! The queue of elements to be processed. //! As the order of booleans doesn't matter, it is used as a LIFO (stack) - std::vector queue; + std::vector queue GUARDED_BY(mutex); //! The number of workers (including the master) that are idle. - int nIdle; + int nIdle GUARDED_BY(mutex){0}; //! The total number of workers (including the master). - int nTotal; + int nTotal GUARDED_BY(mutex){0}; //! The temporary evaluation result. - bool fAllOk; + bool fAllOk GUARDED_BY(mutex){true}; /** * Number of verifications that haven't completed yet. * This includes elements that are no longer queued, but still in the * worker's own batches. */ - unsigned int nTodo; + unsigned int nTodo GUARDED_BY(mutex){0}; //! The maximum number of elements to be processed in one batch unsigned int nBatchSize; + std::vector m_worker_threads; + bool m_request_stop GUARDED_BY(mutex){false}; + /** Internal function that does bulk of the verification work. */ bool Loop(bool fMaster = false) { - boost::condition_variable& cond = fMaster ? condMaster : condWorker; + std::condition_variable& cond = fMaster ? condMaster : condWorker; std::vector vChecks; vChecks.reserve(nBatchSize); unsigned int nNow = 0; bool fOk = true; do { { - boost::unique_lock lock(mutex); + WaitableLock lock(mutex); // first do the clean-up of the previous loop run (allowing us to do it in the same critsect) if (nNow) { fAllOk &= fOk; @@ -85,7 +86,7 @@ class CCheckQueue nTotal++; } // logically, the do loop starts here - while (queue.empty()) { + while (queue.empty() && !m_request_stop) { if (fMaster && nTodo == 0) { nTotal--; bool fRet = fAllOk; @@ -99,6 +100,9 @@ class CCheckQueue cond.wait(lock); // wait nIdle--; } + if (m_request_stop) { + return false; + } // Decide how many work units to process now. // * Do not try to do everything at once, but aim for increasingly smaller batches so // all workers finish approximately simultaneously. @@ -130,10 +134,22 @@ class CCheckQueue //! Create a new check queue explicit CCheckQueue(unsigned int nBatchSizeIn) : nIdle(0), nTotal(0), fAllOk(true), nTodo(0), nBatchSize(nBatchSizeIn) {} - //! Worker thread - void Thread() +//! Create a pool of new worker threads. + void StartWorkerThreads(const int threads_num) { - Loop(); + { + WaitableLock loc(mutex); + nIdle = 0; + nTotal = 0; + fAllOk = true; + } + assert(m_worker_threads.empty()); + for (int n = 0; n < threads_num; ++n) { + m_worker_threads.emplace_back([this, n]() { + RenameThread(strprintf("scriptch.%i", n).c_str()); + Loop(false /* worker thread */); + }); + } } //! Wait until execution finishes, and return whether all evaluations were successful. @@ -145,7 +161,7 @@ class CCheckQueue //! Add a batch of checks to the queue void Add(std::vector& vChecks) { - boost::unique_lock lock(mutex); + WaitableLock lock(mutex); for (T& check : vChecks) { queue.push_back(T()); check.swap(queue.back()); @@ -157,8 +173,27 @@ class CCheckQueue condWorker.notify_all(); } + //! Stop all of the worker threads. + void StopWorkerThreads() + { + { + WaitableLock lock(mutex); + m_request_stop = true; + } + condWorker.notify_all(); + for (std::thread& t : m_worker_threads) { + t.join(); + } + m_worker_threads.clear(); + { + WaitableLock lock(mutex); + m_request_stop = false; + } + } + ~CCheckQueue() { + assert(m_worker_threads.empty()); } }; diff --git a/src/init.cpp b/src/init.cpp index d26047e64a..b6a1816af6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -218,6 +218,7 @@ void Shutdown() // CScheduler/checkqueue threadGroup threadGroup.interrupt_all(); threadGroup.join_all(); + StopScriptCheckWorkerThreads(); // After the threads that potentially access these pointers have been stopped, // destruct and reset all to nullptr. @@ -1230,8 +1231,7 @@ bool AppInitMain() LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { - for (int i=0; i #include #include -#include +#include + #include #include #include @@ -26,6 +26,7 @@ BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup) static const unsigned int QUEUE_BATCH_SIZE = 128; +static const int SCRIPT_CHECK_THREADS = 3; struct FakeCheck { bool operator()() @@ -149,11 +150,8 @@ typedef CCheckQueue FrozenCleanup_Queue; */ static void Correct_Queue_range(std::vector range) { - auto small_queue = std::unique_ptr(new Correct_Queue {QUEUE_BATCH_SIZE}); - boost::thread_group tg; - for (auto x = 0; x < nScriptCheckThreads; ++x) { - tg.create_thread([&]{small_queue->Thread();}); - } + auto small_queue = std::unique_ptr(new Correct_Queue{QUEUE_BATCH_SIZE}); + small_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); // Make vChecks here to save on malloc (this test can be slow...) std::vector vChecks; for (auto i : range) { @@ -168,11 +166,9 @@ static void Correct_Queue_range(std::vector range) BOOST_REQUIRE(control.Wait()); if (FakeCheckCheckCompletion::n_calls != i) { BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i); - BOOST_TEST_MESSAGE("Failure on trial " << i << " expected, got " << FakeCheckCheckCompletion::n_calls); } } - tg.interrupt_all(); - tg.join_all(); + small_queue->StopWorkerThreads(); } /** Test that 0 checks is correct @@ -214,12 +210,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) /** Test that failing checks are caught */ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) { - auto fail_queue = std::unique_ptr(new Failing_Queue {QUEUE_BATCH_SIZE}); - - boost::thread_group tg; - for (auto x = 0; x < nScriptCheckThreads; ++x) { - tg.create_thread([&]{fail_queue->Thread();}); - } + auto fail_queue = std::unique_ptr(new Failing_Queue{QUEUE_BATCH_SIZE}); + fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (size_t i = 0; i < 1001; ++i) { CCheckQueueControl control(fail_queue.get()); @@ -240,18 +232,14 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) BOOST_REQUIRE(success); } } - tg.interrupt_all(); - tg.join_all(); + fail_queue->StopWorkerThreads(); } // Test that a block validation which fails does not interfere with // future blocks, ie, the bad state is cleared. BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) { - auto fail_queue = std::unique_ptr(new Failing_Queue {QUEUE_BATCH_SIZE}); - boost::thread_group tg; - for (auto x = 0; x < nScriptCheckThreads; ++x) { - tg.create_thread([&]{fail_queue->Thread();}); - } + auto fail_queue = std::unique_ptr(new Failing_Queue{QUEUE_BATCH_SIZE}); + fail_queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (auto times = 0; times < 10; ++times) { for (bool end_fails : {true, false}) { @@ -266,8 +254,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) BOOST_REQUIRE(r != end_fails); } } - tg.interrupt_all(); - tg.join_all(); + fail_queue->StopWorkerThreads(); } // Test that unique checks are actually all called individually, rather than @@ -275,12 +262,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) // more than once as well BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) { - auto queue = std::unique_ptr(new Unique_Queue {QUEUE_BATCH_SIZE}); - boost::thread_group tg; - for (auto x = 0; x < nScriptCheckThreads; ++x) { - tg.create_thread([&]{queue->Thread();}); - - } + auto queue = std::unique_ptr(new Unique_Queue{QUEUE_BATCH_SIZE}); + queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); size_t COUNT = 100000; size_t total = COUNT; @@ -294,13 +277,16 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) control.Add(vChecks); } } - bool r = true; - BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT); - for (size_t i = 0; i < COUNT; ++i) - r = r && UniqueCheck::results.count(i) == 1; - BOOST_REQUIRE(r); - tg.interrupt_all(); - tg.join_all(); + { + WaitableLock lock(UniqueCheck::m); + bool r = true; + BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT); + for (size_t i = 0; i < COUNT; ++i) { + r = r && UniqueCheck::results.count(i) == 1; + } + BOOST_REQUIRE(r); + } + queue->StopWorkerThreads(); } @@ -311,11 +297,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) // time could leave the data hanging across a sequence of blocks. BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) { - auto queue = std::unique_ptr(new Memory_Queue {QUEUE_BATCH_SIZE}); - boost::thread_group tg; - for (auto x = 0; x < nScriptCheckThreads; ++x) { - tg.create_thread([&]{queue->Thread();}); - } + auto queue = std::unique_ptr(new Memory_Queue{QUEUE_BATCH_SIZE}); + queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); for (size_t i = 0; i < 1000; ++i) { size_t total = i; { @@ -334,20 +317,16 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) } BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0U); } - tg.interrupt_all(); - tg.join_all(); + queue->StopWorkerThreads(); } // Test that a new verification cannot occur until all checks // have been destructed BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) { - auto queue = std::unique_ptr(new FrozenCleanup_Queue {QUEUE_BATCH_SIZE}); - boost::thread_group tg; + auto queue = std::unique_ptr(new FrozenCleanup_Queue{QUEUE_BATCH_SIZE}); bool fails = false; - for (auto x = 0; x < nScriptCheckThreads; ++x) { - tg.create_thread([&]{queue->Thread();}); - } + queue->StartWorkerThreads(SCRIPT_CHECK_THREADS); std::thread t0([&]() { CCheckQueueControl control(queue.get()); std::vector vChecks(1); @@ -356,7 +335,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) // would get called twice). vChecks[0].should_freeze = true; control.Add(vChecks); - control.Wait(); // Hangs here + bool waitResult = control.Wait(); // Hangs here + assert(waitResult); }); { std::unique_lock l(FrozenCleanupCheck::m); @@ -376,9 +356,8 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) FrozenCleanupCheck::cv.notify_one(); // Wait for control to finish t0.join(); - tg.interrupt_all(); - tg.join_all(); BOOST_REQUIRE(!fails); + queue->StopWorkerThreads(); } diff --git a/src/test/test_tapyrus.cpp b/src/test/test_tapyrus.cpp index 0b4c89f3a7..d107bc1558 100644 --- a/src/test/test_tapyrus.cpp +++ b/src/test/test_tapyrus.cpp @@ -115,8 +115,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha } } nScriptCheckThreads = 3; - for (int i=0; i < nScriptCheckThreads-1; i++) - threadGroup.create_thread(&ThreadScriptCheck); + StartScriptCheckWorkerThreads(nScriptCheckThreads); g_connman = std::unique_ptr(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests. connman = g_connman.get(); peerLogic.reset(new PeerLogicValidation(connman, scheduler)); @@ -126,6 +125,7 @@ TestingSetup::~TestingSetup() { threadGroup.interrupt_all(); threadGroup.join_all(); + StopScriptCheckWorkerThreads(); GetMainSignals().FlushBackgroundCallbacks(); GetMainSignals().UnregisterBackgroundSignalScheduler(); g_connman.reset(); diff --git a/src/validation.cpp b/src/validation.cpp index eb6b7fcd98..d902358dbd 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1821,11 +1821,15 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState& static CCheckQueue scriptcheckqueue(128); -void ThreadScriptCheck() { - RenameThread("tapyrus-scriptch"); - scriptcheckqueue.Thread(); +void StartScriptCheckWorkerThreads(int threads_num) +{ + scriptcheckqueue.StartWorkerThreads(threads_num); } +void StopScriptCheckWorkerThreads() +{ + scriptcheckqueue.StopWorkerThreads(); +} static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex) { AssertLockHeld(cs_main); diff --git a/src/validation.h b/src/validation.h index 6db147ac19..fe72abda00 100644 --- a/src/validation.h +++ b/src/validation.h @@ -262,8 +262,10 @@ bool LoadBlockIndex() EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool LoadChainTip(); /** Unload database information */ void UnloadBlockIndex(); -/** Run an instance of the script checking thread */ -void ThreadScriptCheck(); +/** Run instances of script checking worker threads */ +void StartScriptCheckWorkerThreads(int threads_num); +/** Stop all of the script checking worker threads */ +void StopScriptCheckWorkerThreads(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ bool IsInitialBlockDownload(); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ diff --git a/test/lint/pending-lint-includes.sh b/test/lint/pending-lint-includes.sh index 40a3c11006..f5d2e72168 100755 --- a/test/lint/pending-lint-includes.sh +++ b/test/lint/pending-lint-includes.sh @@ -71,7 +71,6 @@ EXPECTED_BOOST_INCLUDES=( boost/test/unit_test.hpp boost/thread.hpp boost/thread/condition_variable.hpp - boost/thread/mutex.hpp boost/thread/thread.hpp ) From fb234b15af37d1c4054cb96ec63722681d16016f Mon Sep 17 00:00:00 2001 From: Navia Bheeman Date: Thu, 1 Jun 2023 22:38:11 +0530 Subject: [PATCH 03/20] Revert "remove boost::chrono" This reverts commit 434d8d77b8fc8d11c69dca0b3ba4c7e6618e1ef6. --- build-aux/m4/ax_boost_chrono.m4 | 118 ++++++++++++++++++++++++++++++++ configure.ac | 53 +++++++++++++- src/addrman.cpp | 4 +- src/scheduler.cpp | 52 +++++++++----- src/scheduler.h | 16 ++--- src/test/scheduler_tests.cpp | 22 +++--- src/utiltime.cpp | 3 +- 7 files changed, 224 insertions(+), 44 deletions(-) create mode 100644 build-aux/m4/ax_boost_chrono.m4 diff --git a/build-aux/m4/ax_boost_chrono.m4 b/build-aux/m4/ax_boost_chrono.m4 new file mode 100644 index 0000000000..da6478f05a --- /dev/null +++ b/build-aux/m4/ax_boost_chrono.m4 @@ -0,0 +1,118 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_boost_chrono.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_BOOST_CHRONO +# +# DESCRIPTION +# +# Test for Chrono library from the Boost C++ libraries. The macro requires +# a preceding call to AX_BOOST_BASE. Further documentation is available at +# . +# +# This macro calls: +# +# AC_SUBST(BOOST_CHRONO_LIB) +# +# And sets: +# +# HAVE_BOOST_CHRONO +# +# LICENSE +# +# Copyright (c) 2012 Xiyue Deng +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 4 + +AC_DEFUN([AX_BOOST_CHRONO], +[ + AC_ARG_WITH([boost-chrono], + AS_HELP_STRING([--with-boost-chrono@<:@=special-lib@:>@], + [use the Chrono library from boost - it is possible to specify a certain library for the linker + e.g. --with-boost-chrono=boost_chrono-gcc-mt ]), + [ + if test "$withval" = "no"; then + want_boost="no" + elif test "$withval" = "yes"; then + want_boost="yes" + ax_boost_user_chrono_lib="" + else + want_boost="yes" + ax_boost_user_chrono_lib="$withval" + fi + ], + [want_boost="yes"] + ) + + if test "x$want_boost" = "xyes"; then + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_BUILD]) + CPPFLAGS_SAVED="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" + export CPPFLAGS + + LDFLAGS_SAVED="$LDFLAGS" + LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" + export LDFLAGS + + AC_CACHE_CHECK(whether the Boost::Chrono library is available, + ax_cv_boost_chrono, + [AC_LANG_PUSH([C++]) + CXXFLAGS_SAVE=$CXXFLAGS + + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include ]], + [[boost::chrono::system_clock::time_point* time = new boost::chrono::system_clock::time_point; delete time;]])], + ax_cv_boost_chrono=yes, ax_cv_boost_chrono=no) + CXXFLAGS=$CXXFLAGS_SAVE + AC_LANG_POP([C++]) + ]) + if test "x$ax_cv_boost_chrono" = "xyes"; then + AC_SUBST(BOOST_CPPFLAGS) + + AC_DEFINE(HAVE_BOOST_CHRONO,,[define if the Boost::Chrono library is available]) + BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` + + LDFLAGS_SAVE=$LDFLAGS + if test "x$ax_boost_user_chrono_lib" = "x"; then + for libextension in `ls $BOOSTLIBDIR/libboost_chrono*.so* $BOOSTLIBDIR/libboost_chrono*.dylib* $BOOSTLIBDIR/libboost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^lib\(boost_chrono.*\)\.so.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_chrono.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], + [link_chrono="no"]) + done + if test "x$link_chrono" != "xyes"; then + for libextension in `ls $BOOSTLIBDIR/boost_chrono*.dll* $BOOSTLIBDIR/boost_chrono*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_chrono.*\)\.dll.*$;\1;' -e 's;^\(boost_chrono.*\)\.a.*$;\1;'` ; do + ax_lib=${libextension} + AC_CHECK_LIB($ax_lib, exit, + [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], + [link_chrono="no"]) + done + fi + + else + for ax_lib in $ax_boost_user_chrono_lib boost_chrono-$ax_boost_user_chrono_lib; do + AC_CHECK_LIB($ax_lib, exit, + [BOOST_CHRONO_LIB="-l$ax_lib"; AC_SUBST(BOOST_CHRONO_LIB) link_chrono="yes"; break], + [link_chrono="no"]) + done + + fi + if test "x$ax_lib" = "x"; then + AC_MSG_ERROR(Could not find a version of the library!) + fi + if test "x$link_chrono" = "xno"; then + AC_MSG_ERROR(Could not link against $ax_lib !) + fi + fi + + CPPFLAGS="$CPPFLAGS_SAVED" + LDFLAGS="$LDFLAGS_SAVED" + fi +]) diff --git a/configure.ac b/configure.ac index bcf5d94c17..e877b511ee 100644 --- a/configure.ac +++ b/configure.ac @@ -912,6 +912,7 @@ fi AX_BOOST_SYSTEM AX_BOOST_FILESYSTEM AX_BOOST_THREAD +AX_BOOST_CHRONO dnl Boost 1.56 through 1.62 allow using std::atomic instead of its own atomic dnl counter implementations. In 1.63 and later the std::atomic approach is default. @@ -959,7 +960,7 @@ fi if test x$use_boost = xyes; then -BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB " +BOOST_LIBS="$BOOST_LDFLAGS $BOOST_SYSTEM_LIB $BOOST_FILESYSTEM_LIB $BOOST_THREAD_LIB $BOOST_CHRONO_LIB" dnl If boost (prior to 1.57) was built without c++11, it emulated scoped enums @@ -997,6 +998,56 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ LIBS="$TEMP_LIBS" CPPFLAGS="$TEMP_CPPFLAGS" +dnl Boost >= 1.50 uses sleep_for rather than the now-deprecated sleep, however +dnl it was broken from 1.50 to 1.52 when backed by nanosleep. Use sleep_for if +dnl a working version is available, else fall back to sleep. sleep was removed +dnl after 1.56. +dnl If neither is available, abort. +TEMP_LIBS="$LIBS" +LIBS="$BOOST_LIBS $LIBS" +TEMP_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + #if BOOST_VERSION >= 105000 && (!defined(BOOST_HAS_NANOSLEEP) || BOOST_VERSION >= 105200) + boost::this_thread::sleep_for(boost::chrono::milliseconds(0)); + #else + choke me + #endif + ]])], + [boost_sleep=yes; + AC_DEFINE(HAVE_WORKING_BOOST_SLEEP_FOR, 1, [Define this symbol if boost sleep_for works])], + [boost_sleep=no]) +LIBS="$TEMP_LIBS" +CPPFLAGS="$TEMP_CPPFLAGS" + +if test x$boost_sleep != xyes; then +TEMP_LIBS="$LIBS" +LIBS="$BOOST_LIBS $LIBS" +TEMP_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + #include + ]],[[ + #if BOOST_VERSION <= 105600 + boost::this_thread::sleep(boost::posix_time::milliseconds(0)); + #else + choke me + #endif + ]])], + [boost_sleep=yes; AC_DEFINE(HAVE_WORKING_BOOST_SLEEP, 1, [Define this symbol if boost sleep works])], + [boost_sleep=no]) +LIBS="$TEMP_LIBS" +CPPFLAGS="$TEMP_CPPFLAGS" +fi + +if test x$boost_sleep != xyes; then + AC_MSG_ERROR(No working boost sleep implementation found.) +fi fi diff --git a/src/addrman.cpp b/src/addrman.cpp index 1bfcb1ee55..093b263ab3 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -9,8 +9,6 @@ #include #include -#include - int CAddrInfo::GetTriedBucket(const uint256& nKey) const { uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetCheapHash(); @@ -62,7 +60,7 @@ double CAddrInfo::GetChance(int64_t nNow) const fChance *= 0.01; // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages. - fChance *= std::pow(0.66, std::min(nAttempts, 8)); + fChance *= pow(0.66, std::min(nAttempts, 8)); return fChance; } diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 2d76d1186d..d2b882d842 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -9,10 +9,6 @@ #include #include -#include - -#include -#include CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stopWhenEmpty(false) { @@ -23,9 +19,19 @@ CScheduler::~CScheduler() assert(nThreadsServicingQueue == 0); } + +#if BOOST_VERSION < 105000 +static boost::system_time toPosixTime(const boost::chrono::system_clock::time_point& t) +{ + // Creating the posix_time using from_time_t loses sub-second precision. So rather than exporting the time_point to time_t, + // start with a posix_time at the epoch (0) and add the milliseconds that have passed since then. + return boost::posix_time::from_time_t(0) + boost::posix_time::milliseconds(boost::chrono::duration_cast(t.time_since_epoch()).count()); +} +#endif + void CScheduler::serviceQueue() { - WaitableLock lock(newTaskMutex); + boost::unique_lock lock(newTaskMutex); ++nThreadsServicingQueue; // newTaskMutex is locked throughout this loop EXCEPT @@ -34,7 +40,7 @@ void CScheduler::serviceQueue() while (!shouldStop()) { try { if (!shouldStop() && taskQueue.empty()) { - reverse_lock rlock(lock); + reverse_lock > rlock(lock); } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. @@ -43,12 +49,22 @@ void CScheduler::serviceQueue() // Wait until either there is a new task, or until // the time of the first item on the queue: + +// wait_until needs boost 1.50 or later; older versions have timed_wait: +#if BOOST_VERSION < 105000 + while (!shouldStop() && !taskQueue.empty() && + newTaskScheduled.timed_wait(lock, toPosixTime(taskQueue.begin()->first))) { + // Keep waiting until timeout + } +#else + // Some boost versions have a conflicting overload of wait_until that returns void. + // Explicitly use a template here to avoid hitting that overload. while (!shouldStop() && !taskQueue.empty()) { - std::chrono::steady_clock::time_point timeToWaitFor = taskQueue.begin()->first; - if (newTaskScheduled.wait_until(lock, timeToWaitFor) == std::cv_status::timeout) + boost::chrono::system_clock::time_point timeToWaitFor = taskQueue.begin()->first; + if (newTaskScheduled.wait_until<>(lock, timeToWaitFor) == boost::cv_status::timeout) break; // Exit loop after timeout, it means we reached the time of the event } - +#endif // If there are multiple threads, the queue can empty while we're waiting (another // thread may service the task we were waiting on). if (shouldStop() || taskQueue.empty()) @@ -60,7 +76,7 @@ void CScheduler::serviceQueue() { // Unlock before calling f, so it can reschedule itself or another task // without deadlocking: - reverse_lock rlock(lock); + reverse_lock > rlock(lock); f(); } } catch (...) { @@ -75,7 +91,7 @@ void CScheduler::serviceQueue() void CScheduler::stop(bool drain) { { - WaitableLock lock(newTaskMutex); + boost::unique_lock lock(newTaskMutex); if (drain) stopWhenEmpty = true; else @@ -84,10 +100,10 @@ void CScheduler::stop(bool drain) newTaskScheduled.notify_all(); } -void CScheduler::schedule(CScheduler::Function f, std::chrono::steady_clock::time_point t) +void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t) { { - WaitableLock lock(newTaskMutex); + boost::unique_lock lock(newTaskMutex); taskQueue.insert(std::make_pair(t, f)); } newTaskScheduled.notify_one(); @@ -95,7 +111,7 @@ void CScheduler::schedule(CScheduler::Function f, std::chrono::steady_clock::tim void CScheduler::scheduleFromNow(CScheduler::Function f, int64_t deltaMilliSeconds) { - schedule(f, std::chrono::steady_clock::now() + std::chrono::milliseconds(deltaMilliSeconds)); + schedule(f, boost::chrono::system_clock::now() + boost::chrono::milliseconds(deltaMilliSeconds)); } static void Repeat(CScheduler* s, CScheduler::Function f, int64_t deltaMilliSeconds) @@ -109,10 +125,10 @@ void CScheduler::scheduleEvery(CScheduler::Function f, int64_t deltaMilliSeconds scheduleFromNow(std::bind(&Repeat, this, f, deltaMilliSeconds), deltaMilliSeconds); } -size_t CScheduler::getQueueInfo(std::chrono::steady_clock::time_point &first, - std::chrono::steady_clock::time_point &last) const +size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first, + boost::chrono::system_clock::time_point &last) const { - WaitableLock lock(newTaskMutex); + boost::unique_lock lock(newTaskMutex); size_t result = taskQueue.size(); if (!taskQueue.empty()) { first = taskQueue.begin()->first; @@ -122,7 +138,7 @@ size_t CScheduler::getQueueInfo(std::chrono::steady_clock::time_point &first, } bool CScheduler::AreThreadsServicingQueue() const { - WaitableLock lock(newTaskMutex); + boost::unique_lock lock(newTaskMutex); return nThreadsServicingQueue; } diff --git a/src/scheduler.h b/src/scheduler.h index 79f473bb40..a1c064ef1e 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -10,11 +10,9 @@ // boost::thread / boost::chrono should be ported to std::thread / std::chrono // when we support C++11. // - +#include #include #include -#include -#include #include @@ -45,7 +43,7 @@ class CScheduler typedef std::function Function; // Call func at/after time t - void schedule(Function f, std::chrono::steady_clock::time_point t=std::chrono::steady_clock::now()); + void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now()); // Convenience method: call f once deltaSeconds from now void scheduleFromNow(Function f, int64_t deltaMilliSeconds); @@ -70,16 +68,16 @@ class CScheduler // Returns number of tasks waiting to be serviced, // and first and last task times - size_t getQueueInfo(std::chrono::steady_clock::time_point &first, - std::chrono::steady_clock::time_point &last) const; + size_t getQueueInfo(boost::chrono::system_clock::time_point &first, + boost::chrono::system_clock::time_point &last) const; // Returns true if there are threads actively running in serviceQueue() bool AreThreadsServicingQueue() const; private: - std::multimap taskQueue; - std::condition_variable newTaskScheduled; - mutable std::mutex newTaskMutex; + std::multimap taskQueue; + boost::condition_variable newTaskScheduled; + mutable boost::mutex newTaskMutex; int nThreadsServicingQueue; bool stopRequested; bool stopWhenEmpty; diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 0afc08943b..edc43dd870 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -13,13 +13,13 @@ BOOST_AUTO_TEST_SUITE(scheduler_tests) -static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, std::chrono::steady_clock::time_point rescheduleTime) +static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, boost::chrono::system_clock::time_point rescheduleTime) { { boost::unique_lock lock(mutex); counter += delta; } - std::chrono::steady_clock::time_point noTime = std::chrono::steady_clock::time_point::min(); + boost::chrono::system_clock::time_point noTime = boost::chrono::system_clock::time_point::min(); if (rescheduleTime != noTime) { CScheduler::Function f = std::bind(µTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime); s.schedule(f, rescheduleTime); @@ -28,7 +28,7 @@ static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delt static void MicroSleep(uint64_t n) { - std::this_thread::sleep_for(std::chrono::microseconds(n)); + boost::this_thread::sleep_for(boost::chrono::microseconds(n)); } BOOST_AUTO_TEST_CASE(manythreads) @@ -52,15 +52,15 @@ BOOST_AUTO_TEST_CASE(manythreads) auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000] auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000] - std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); - std::chrono::steady_clock::time_point now = start; - std::chrono::steady_clock::time_point first, last; + boost::chrono::system_clock::time_point start = boost::chrono::system_clock::now(); + boost::chrono::system_clock::time_point now = start; + boost::chrono::system_clock::time_point first, last; size_t nTasks = microTasks.getQueueInfo(first, last); BOOST_CHECK(nTasks == 0); for (int i = 0; i < 100; ++i) { - std::chrono::steady_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); - std::chrono::steady_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); + boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); + boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), @@ -78,14 +78,14 @@ BOOST_AUTO_TEST_CASE(manythreads) microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); MicroSleep(600); - now = std::chrono::steady_clock::now(); + now = boost::chrono::system_clock::now(); // More threads and more tasks: for (int i = 0; i < 5; i++) microThreads.create_thread(std::bind(&CScheduler::serviceQueue, µTasks)); for (int i = 0; i < 100; i++) { - std::chrono::steady_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng)); - std::chrono::steady_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng)); + boost::chrono::system_clock::time_point t = now + boost::chrono::microseconds(randomMsec(rng)); + boost::chrono::system_clock::time_point tReschedule = now + boost::chrono::microseconds(500 + randomMsec(rng)); int whichCounter = zeroToNine(rng); CScheduler::Function f = std::bind(µTask, std::ref(microTasks), std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]), diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 2bb3344618..694de86d9a 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -62,7 +61,7 @@ int64_t GetSystemTimeInSeconds() void MilliSleep(int64_t n) { - std::this_thread::sleep_for(std::chrono::milliseconds(n)); + boost::this_thread::sleep_for(boost::chrono::milliseconds(n)); } std::string FormatISO8601DateTime(int64_t nTime) { From e6bf36329aaaa269324ca68e4cdd49abb1d61a22 Mon Sep 17 00:00:00 2001 From: Navia Bheeman Date: Thu, 1 Jun 2023 22:28:46 +0530 Subject: [PATCH 04/20] remove boost::thread and use std::thread --- src/bench/block_assemble.cpp | 9 +++++---- src/checkqueue.h | 14 +++++++------- src/init.cpp | 12 +++++------- src/interfaces/node.cpp | 1 - src/rpc/blockchain.cpp | 5 ++--- src/scheduler.cpp | 6 ++++++ src/scheduler.h | 20 ++++++++++---------- src/sync.h | 10 +++++++--- src/tapyrus-cli.cpp | 3 --- src/tapyrus-tx.cpp | 4 ---- src/test/checkqueue_tests.cpp | 17 ++++++++++------- src/test/cuckoocache_tests.cpp | 2 ++ src/test/reverselock_tests.cpp | 2 ++ src/test/scheduler_tests.cpp | 19 ++++++++++++------- src/test/test_tapyrus.cpp | 5 ++--- src/test/test_tapyrus.h | 2 -- src/test/validation_block_tests.cpp | 8 +++++--- src/util.h | 6 ------ 18 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 43cc919ad1..3022cafb64 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -86,14 +86,14 @@ static void AssembleBlock(benchmark::State& state) InitSignatureCache(); InitScriptExecutionCache(); - boost::thread_group thread_group; + std::vector thread_group; CScheduler scheduler; { ::pblocktree.reset(new CBlockTreeDB(1 << 20, true)); ::pcoinsdbview.reset(new CCoinsViewDB(1 << 23, true)); ::pcoinsTip.reset(new CCoinsViewCache(pcoinsdbview.get())); - thread_group.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); + thread_group.emplace_back(std::bind(&CScheduler::serviceQueue, &scheduler)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); LoadGenesisBlock(); CValidationState state; @@ -130,8 +130,9 @@ static void AssembleBlock(benchmark::State& state) PrepareBlock(SCRIPT_PUB); } - thread_group.interrupt_all(); - thread_group.join_all(); + for (auto& thread: thread_group) { + if (thread.joinable()) thread.join(); + } GetMainSignals().FlushBackgroundCallbacks(); GetMainSignals().UnregisterBackgroundSignalScheduler(); } diff --git a/src/checkqueue.h b/src/checkqueue.h index e3a1f03efc..da826baa3a 100644 --- a/src/checkqueue.h +++ b/src/checkqueue.h @@ -39,29 +39,29 @@ class CCheckQueue //! The queue of elements to be processed. //! As the order of booleans doesn't matter, it is used as a LIFO (stack) - std::vector queue GUARDED_BY(mutex); + std::vector queue; //! The number of workers (including the master) that are idle. - int nIdle GUARDED_BY(mutex){0}; + int nIdle; //! The total number of workers (including the master). - int nTotal GUARDED_BY(mutex){0}; + int nTotal; //! The temporary evaluation result. - bool fAllOk GUARDED_BY(mutex){true}; + bool fAllOk; /** * Number of verifications that haven't completed yet. * This includes elements that are no longer queued, but still in the * worker's own batches. */ - unsigned int nTodo GUARDED_BY(mutex){0}; + unsigned int nTodo; //! The maximum number of elements to be processed in one batch unsigned int nBatchSize; std::vector m_worker_threads; - bool m_request_stop GUARDED_BY(mutex){false}; + bool m_request_stop; /** Internal function that does bulk of the verification work. */ bool Loop(bool fMaster = false) @@ -129,7 +129,7 @@ class CCheckQueue public: //! Mutex to ensure only one concurrent CCheckQueueControl - boost::mutex ControlMutex; + std::mutex ControlMutex; //! Create a new check queue explicit CCheckQueue(unsigned int nBatchSizeIn) : nIdle(0), nTotal(0), fAllOk(true), nTodo(0), nBatchSize(nBatchSizeIn) {} diff --git a/src/init.cpp b/src/init.cpp index b6a1816af6..c90437422f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -60,7 +60,8 @@ #include #include #include -#include + +#include #if ENABLE_ZMQ #include @@ -166,7 +167,6 @@ class CCoinsViewErrorCatcher final : public CCoinsViewBacked static std::unique_ptr pcoinscatcher; static std::unique_ptr globalVerifyHandle; -static boost::thread_group threadGroup; static CScheduler scheduler; void Interrupt() @@ -215,9 +215,7 @@ void Shutdown() StopTorControl(); // After everything has been shut down, but before things get flushed, stop the - // CScheduler/checkqueue threadGroup - threadGroup.interrupt_all(); - threadGroup.join_all(); + // CScheduler/checkqueue, scheduler and load block thread. StopScriptCheckWorkerThreads(); // After the threads that potentially access these pointers have been stopped, @@ -1236,7 +1234,7 @@ bool AppInitMain() // Start the lightweight task scheduler thread CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler); - threadGroup.create_thread(std::bind(&TraceThread, "scheduler", serviceLoop)); + scheduler.m_service_thread = std::thread(TraceThread, "scheduler", serviceLoop); // Gather some entropy once per minute. scheduler.scheduleEvery([]{ @@ -1634,7 +1632,7 @@ bool AppInitMain() vImportFiles.push_back(strFile); } - threadGroup.create_thread(std::bind(&ThreadImport, vImportFiles, fReloadxfield)); + scheduler.m_load_block = std::thread(std::bind(&ThreadImport, vImportFiles, fReloadxfield)); // Wait for genesis block to be processed { diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 462ce4ef01..0c6e41b974 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -41,7 +41,6 @@ #endif #include -#include #include namespace interfaces { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1fc818fc1f..339092d2bc 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -40,7 +40,6 @@ #include #include -#include // boost::thread::interrupt #include #include @@ -839,7 +838,7 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) uint256 prevkey; std::map outputs; while (pcursor->Valid()) { - boost::this_thread::interruption_point(); + std::this_thread::yield(); COutPoint key; Coin coin; if (pcursor->GetKey(key) && pcursor->GetValue(coin)) { @@ -1832,7 +1831,7 @@ bool FindScriptPubKey(std::atomic& scan_progress, const std::atomic& Coin coin; if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false; if (++count % 8192 == 0) { - boost::this_thread::interruption_point(); + std::this_thread::yield(); if (should_abort) { // allow to abort the scan via the abort reference return false; diff --git a/src/scheduler.cpp b/src/scheduler.cpp index d2b882d842..de9ccdffa7 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -98,6 +98,12 @@ void CScheduler::stop(bool drain) stopRequested = true; } newTaskScheduled.notify_all(); + + if (m_service_thread.joinable()) + m_service_thread.join(); + + if(m_load_block.joinable()) + m_load_block.join(); } void CScheduler::schedule(CScheduler::Function f, boost::chrono::system_clock::time_point t) diff --git a/src/scheduler.h b/src/scheduler.h index a1c064ef1e..eb8a25d959 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -5,14 +5,12 @@ #ifndef BITCOIN_SCHEDULER_H #define BITCOIN_SCHEDULER_H -// -// NOTE: -// boost::thread / boost::chrono should be ported to std::thread / std::chrono -// when we support C++11. -// -#include -#include + + #include +#include +#include +#include #include @@ -25,7 +23,7 @@ // CScheduler* s = new CScheduler(); // s->scheduleFromNow(doSomething, 11); // Assuming a: void doSomething() { } // s->scheduleFromNow(std::bind(Class::func, this, argument), 3); -// boost::thread* t = new boost::thread(std::bind(CScheduler::serviceQueue, s)); +// std::thread* t = new std::thread(std::bind(CScheduler::serviceQueue, s)); // // ... then at program shutdown, clean up the thread running serviceQueue: // t->interrupt(); @@ -40,6 +38,9 @@ class CScheduler CScheduler(); ~CScheduler(); + std::thread m_service_thread; + std::thread m_load_block; + typedef std::function Function; // Call func at/after time t @@ -57,8 +58,7 @@ class CScheduler // To keep things as simple as possible, there is no unschedule. - // Services the queue 'forever'. Should be run in a thread, - // and interrupted using boost::interrupt_thread + // Services the queue 'forever'. Should be run in a thread void serviceQueue(); // Tell any threads running serviceQueue to stop as soon as they're diff --git a/src/sync.h b/src/sync.h index 11c1253757..f60e506174 100644 --- a/src/sync.h +++ b/src/sync.h @@ -80,6 +80,7 @@ void DeleteLock(void* cs); #else void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} void static inline LeaveCritical() {} +void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {} void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {} void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} void static inline DeleteLock(void* cs) {} @@ -178,17 +179,20 @@ class SCOPED_LOCKABLE CCriticalBlock #define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) #define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) #define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) +#define WAIT_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__) #define ENTER_CRITICAL_SECTION(cs) \ { \ - EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ + EnterCritical(#cs, __FILE__, __LINE__, (&cs)); \ (cs).lock(); \ } #define LEAVE_CRITICAL_SECTION(cs) \ { \ - (cs).unlock(); \ - LeaveCritical(); \ + std::string lockname; \ + CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \ + (cs).unlock(); \ + LeaveCritical(); \ } class CSemaphore diff --git a/src/tapyrus-cli.cpp b/src/tapyrus-cli.cpp index 8a7f69db11..fdba571501 100644 --- a/src/tapyrus-cli.cpp +++ b/src/tapyrus-cli.cpp @@ -495,9 +495,6 @@ static int CommandLineRPC(int argc, char *argv[]) } } while (fWait); } - catch (const boost::thread_interrupted&) { - throw; - } catch (const std::exception& e) { strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; diff --git a/src/tapyrus-tx.cpp b/src/tapyrus-tx.cpp index 7d5f01739c..92553f4f85 100644 --- a/src/tapyrus-tx.cpp +++ b/src/tapyrus-tx.cpp @@ -800,10 +800,6 @@ static int CommandLineRawTx(int argc, char* argv[]) OutputTx(tx); } - - catch (const boost::thread_interrupted&) { - throw; - } catch (const std::exception& e) { strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index f8c4357b6b..449e6673b8 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -366,11 +365,11 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) { auto queue = std::unique_ptr(new Standard_Queue{QUEUE_BATCH_SIZE}); { - boost::thread_group tg; + std::vector tg; std::atomic nThreads {0}; std::atomic fails {0}; for (size_t i = 0; i < 3; ++i) { - tg.create_thread( + tg.emplace_back( [&]{ CCheckQueueControl control(queue.get()); // While sleeping, no other thread should execute to this point @@ -379,11 +378,13 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) fails += observed != nThreads; }); } - tg.join_all(); + for (auto& thread: tg) { + if (thread.joinable()) thread.join(); + } BOOST_REQUIRE_EQUAL(fails, 0); } { - boost::thread_group tg; + std::vector tg; std::mutex m; std::condition_variable cv; bool has_lock{false}; @@ -392,7 +393,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) bool done_ack{false}; { std::unique_lock l(m); - tg.create_thread([&]{ + tg.emplace_back([&]{ CCheckQueueControl control(queue.get()); std::unique_lock ll(m); has_lock = true; @@ -418,7 +419,9 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) cv.notify_one(); BOOST_REQUIRE(!fails); } - tg.join_all(); + for (auto& thread: tg) { + if (thread.joinable()) thread.join(); + } } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index 9fab6d1e7f..a59bd2d6a1 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -3,6 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include +#include #include #include