-
Notifications
You must be signed in to change notification settings - Fork 36.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CCheckQueue Unit Tests #9497
CCheckQueue Unit Tests #9497
Conversation
src/test/checkqueue_tests.cpp
Outdated
@@ -0,0 +1,395 @@ | |||
// Copyright (c) 2012-2015 The Bitcoin Core developers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
couple of years out of date :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will fix!
641f5b8
to
f4f1426
Compare
Sorry for the line noise; the earlier build error should be addressed now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall great and very clever tests (thread safety one was fun to figure out). Added a bunch of minor comments. The only two comments I would really urge you to consider are the ones on the Memory and FrozenCleanup tests. I think it would be good to check same conditions without allocating large chunks of memory or doing 1-second busy loops so these tests can be more efficient and more reliable.
src/test/checkqueue_tests.cpp
Outdated
#include <unordered_set> | ||
#include <memory> | ||
#include "random.h" | ||
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment noting BasicTestingSetup can't be used because it doesn't set nScriptCheckThreads.
src/test/checkqueue_tests.cpp
Outdated
*/ | ||
void Correct_Queue_range(std::vector<size_t> range) | ||
{ | ||
auto small_queue = std::shared_ptr<Correct_Queue>(new Correct_Queue {128}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe declare 128 and any other common parameters as constants above.
src/test/checkqueue_tests.cpp
Outdated
while (total) { | ||
size_t r = GetRand(10); | ||
std::vector<FakeCheckCheckCompletion> vChecks; | ||
for (size_t k = 0; k < r && total; k++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe replace the loop with vCheck.resize(min(total, r)).
src/test/checkqueue_tests.cpp
Outdated
*/ | ||
void Correct_Queue_range(std::vector<size_t> range) | ||
{ | ||
auto small_queue = std::shared_ptr<Correct_Queue>(new Correct_Queue {128}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is small_queue a shared pointer, not a unique pointer or just plain stack variable? Maybe add a comment explaining. Also, you could probably use make_shared if it does need to be a shared pointer.
src/test/checkqueue_tests.cpp
Outdated
} | ||
result[end_fails ? 0 : 1] = control.Wait(); | ||
} | ||
BOOST_CHECK(!result[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would seem more direct to just BOOST_CHECK the control.Wait() call instead of putting the results in an intermediate array. This way is ok too, though.
src/test/checkqueue_tests.cpp
Outdated
auto small_queue = std::shared_ptr<Correct_Queue>(new Correct_Queue {128}); | ||
boost::thread_group tg; | ||
for (auto x = 0; x < nScriptCheckThreads; ++x) { | ||
tg.create_thread([=]{small_queue->Thread();}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replacing [=] with [&] might allow small_queue not to be a shared_ptr.
src/test/checkqueue_tests.cpp
Outdated
bool r = true; | ||
for (size_t i = 0; i < COUNT; ++i) | ||
r = r && UniqueCheck::results.count(i) == 1; | ||
BOOST_REQUIRE(r); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also check that UniqueCheck::results.size == COUNT.
src/test/checkqueue_tests.cpp
Outdated
|
||
// Test that blocks which might allocate lots of memory free their memory agressively. | ||
// | ||
// This test attempts to catch a pathalogical case where by lazily freeing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pathological (spelling)
src/test/checkqueue_tests.cpp
Outdated
// checks might mean leaving a check un-swapped out, and decreasing by 1 each | ||
// time could leave the data hanging across a sequence of blocks. | ||
// | ||
// This test (failing) is dependent on not being able to handle |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of having the test be nondeterministic in this way, would anything be lost if you had the MemoryCheck constructor increment a static counter when passed a true arg, and the MemoryCheck destructor decrement the counter if the object was constructed with a true arg. Then you could detect the error case explicitly by checking the counter, and not have to allocate big chunks of memory.
src/test/checkqueue_tests.cpp
Outdated
// | ||
// Note that this cannot cause a spurious failure, only could mean that | ||
// the test doesn't actually end up checking that control waited. | ||
MilliSleep(1000); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you could easily make this test deterministic as well, eliminating the long sleep and the while (frozen) busy loops. Would just need to have ~FrozenCleanupCheck increment a counter and signal a conditional variable so you could wait here for enough jobs to be frozen, and then do the boost check. Then this could signal another condition variable to unfreeze the jobs. I think it would be worth changing this to make the test more efficient and reliable.
95316c2
to
4beddec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK d44af13ddc3615686a3e76cf9a3412999db0d692. Left one minor comment, feel free to ignore.
src/test/checkqueue_tests.cpp
Outdated
while (!done && !fails2) { | ||
fails2 = queue->ControlMutex.try_lock(); | ||
std::mutex m; | ||
bool has_lock {false}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be clearer to replace all these bools with an enum like { STARTED, TRY_LOCK, TRY_LOCK_DONE, DONE, DONE_ACK }.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going to ignore this, I don't think it makes it more clear (to me, it's easier to debug several variables). If someone disagrees strongly, will change.
ACK, needs squashing |
d44af13
to
747c5f3
Compare
Squashed! |
747c5f3
to
5f84b90
Compare
Rebased to be on top of #9495. |
5f84b90
to
0949835
Compare
src/test/checkqueue_tests.cpp
Outdated
control.Add(vChecks); | ||
} | ||
} | ||
BOOST_REQUIRE(MemoryCheck::fake_allocated_memory == 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MemoryCheck struct destructor does not --, so this should not be == 0 unless no MemoryCheck constructors are ever called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it was the latter. The for loop never made anything (i = 9999; i<9999). Will fix :)
src/test/checkqueue_tests.cpp
Outdated
}; | ||
~MemoryCheck(){ | ||
if (b) { | ||
fake_allocated_memory += 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fake_allocated_memory -= 1
src/test/checkqueue_tests.cpp
Outdated
BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) | ||
{ | ||
auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE}); | ||
std::array<FailingCheck, 100> checks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable.
src/test/checkqueue_tests.cpp
Outdated
// Wait until the queue has finished all jobs and frozen | ||
FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;}); | ||
// Try to get control of the queue a bunch of times | ||
for (auto x = 0; x < 100; ++x) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doing !fails && x < 100
here and simply fails = queue->ControlMutex.try_lock();
would break iteration on first fail rather than iterate over all 100 (e.g. if first try_lock()
fails).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure..
src/test/checkqueue_tests.cpp
Outdated
// Wait for thread to get the lock | ||
cv.wait(l, [&](){return has_lock;}); | ||
bool fails = false; | ||
for (auto x = 0; x < 100; ++x) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here with !fails && x < 100
as above.
0949835
to
96c7f2c
Compare
Fixed the issues that @kallewoof raised, and squashed. Unsquashed preserved here https://github.com/JeremyRubin/bitcoin/tree/checkqueue-tests-unsquashed. |
utACK 96c7f2c I'm a bit concerned about non-deterministic behavior in tests as this tends to be a pain when you do run into a problem. Or is this fixed seed / PRNG so that the numbers are always the same each time? (for |
I could make them deterministic if that's desirable, but realistically these tests are already non-deterministic by virtue of being multithreaded. None of the uses of GetRand are particularly dangerous here, although perhaps they area a little slower than could be. |
I think that would be desirable, even if the multithreading makes it not 100%. |
ACK 96c7f2c |
96c7f2c Add CheckQueue Tests (Jeremy Rubin) e207342 Fix CCheckQueue IsIdle (potential) race condition and remove dangerous constructors. (Jeremy Rubin) Tree-SHA512: 5989743ad0f8b08998335e7ca9256e168fa319053f91b9dece9dbb134885bef7753b567b591acc7135785f23d19799ed7e6375917f59fe0178d389e961633d62
… into 0.14.2_fixes
96c7f2c Add CheckQueue Tests (Jeremy Rubin) e207342 Fix CCheckQueue IsIdle (potential) race condition and remove dangerous constructors. (Jeremy Rubin) Tree-SHA512: 5989743ad0f8b08998335e7ca9256e168fa319053f91b9dece9dbb134885bef7753b567b591acc7135785f23d19799ed7e6375917f59fe0178d389e961633d62
96c7f2c Add CheckQueue Tests (Jeremy Rubin) e207342 Fix CCheckQueue IsIdle (potential) race condition and remove dangerous constructors. (Jeremy Rubin) Tree-SHA512: 5989743ad0f8b08998335e7ca9256e168fa319053f91b9dece9dbb134885bef7753b567b591acc7135785f23d19799ed7e6375917f59fe0178d389e961633d62
96c7f2c Add CheckQueue Tests (Jeremy Rubin) e207342 Fix CCheckQueue IsIdle (potential) race condition and remove dangerous constructors. (Jeremy Rubin) Tree-SHA512: 5989743ad0f8b08998335e7ca9256e168fa319053f91b9dece9dbb134885bef7753b567b591acc7135785f23d19799ed7e6375917f59fe0178d389e961633d62
…s constructors. zcash: cherry picked from commit e207342 zcash: bitcoin/bitcoin#9497
zcash: cherry picked from commit 96c7f2c zcash: bitcoin/bitcoin#9497
Bitcoin 0.15 locking PRs These are locking changes from upstream (bitcoin core) release 0.15, oldest to newest (when merged to the master branch). Each commit also includes a reference both to the PR and the upstream commit. - bitcoin/bitcoin#9497
This PR builds on #9495 to unit test the CCheckQueue for correctness.
The cases covered in these tests are: