error: in "validationinterface_tests/unregister_all_during_call": check destroyed has failed #28146

issue maflcko opened this issue on July 25, 2023
  1. maflcko commented at 10:15 AM on July 25, 2023: member

    Is there an existing issue for this?

    • I have searched the existing issues

    Current behaviour

    check failed

    Expected behaviour

    not fail

    Steps to reproduce

    ?

    Relevant log output

    https://cirrus-ci.com/task/4769037708689408?logs=ci#L3768

    test/validationinterface_tests.cpp(93): error: in "validationinterface_tests/unregister_all_during_call": check destroyed has failed
    

    How did you obtain Bitcoin Core

    Compiled from source

    What version of Bitcoin Core are you using?

    current master

    Operating system and version

    arm CI

    Machine specifications

    No response

  2. maflcko commented at 10:16 AM on July 25, 2023: member

    TestInterface::Call(); should be blocking on the destructor, which sets destroyed, so I have no idea how this could happen.

  3. maflcko added the label Tests on Jul 25, 2023
  4. Crypt-iQ commented at 2:16 PM on July 25, 2023: contributor

    I was able to reproduce this by adding some sleeps:

    <details> <summary>diff on bc4f6b13feb29146b7e10e86f93dc7f6fb6937f2 </summary> <br>

    diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
    index d344c8bfb..bb645ae94 100644
    --- a/src/validationinterface.cpp
    +++ b/src/validationinterface.cpp
    @@ -17,6 +17,9 @@
     #include <unordered_map>
     #include <utility>
     
    +#include <chrono>
    +#include <thread>
    +
     std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept;
     
     /**
    @@ -86,7 +89,9 @@ public:
                 {
                     REVERSE_LOCK(lock);
                     f(*it->callbacks);
    +                std::this_thread::sleep_for(1ms);
                 }
    +            LogPrintf("it->count %v\n", it->count);
                 it = --it->count ? std::next(it) : m_list.erase(it);
             }
         }
    @@ -182,6 +187,7 @@ void SyncWithValidationInterfaceQueue()
             auto local_name = (name);                              \
             LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__);  \
             m_internals->m_schedulerClient.AddToProcessQueue([=] { \
    +            std::this_thread::sleep_for(800us);                  \
                 LOG_EVENT(fmt, local_name, __VA_ARGS__);           \
                 event();                                           \
             });                                                    \
    @@ -194,7 +200,6 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
         // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
         // the chain actually updates. One way to ensure this is for the caller to invoke this signal
         // in the same critical section where the chain is updated
    -
         auto event = [pindexNew, pindexFork, fInitialDownload, this] {
             m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
         };
    

    </details>

    In the CI logs, UpdatedBlockTip and BlockChecked run at ~ the same time. I think what happens is:

    • BlockChecked calls Iterate which increments it->count (count = 2) with m_mutex
    • m_on_call gets called without m_mutex
    • In the scheduler thread, Iterate is called and increments it->count (count = 3) with m_mutex. (UpdatedBlockTip runs in the scheduler thread). Then m_mutex is released to call UpdatedBlockTip
    • m_on_call calls UnregisterAllValidationInterfaces which will decrement it->count (count = 2) with m_mutex.
    • m_on_call calls UnregisterAllValidationInterfaces which is a no-op.
    • The BlockChecked callback finishes and since the count = 2, the element isn't erased so the destructor isn't called. This fails the test
  5. maflcko commented at 3:30 PM on July 25, 2023: member

    UpdatedBlockTip runs in the scheduler thread

    Thanks. That sounds like a bug. The unit test should be single threaded, or alternatively drop all async notifications before starting.

  6. maflcko commented at 3:36 PM on July 25, 2023: member

    Let's continue discussion in https://github.com/bitcoin/bitcoin/pull/28150

  7. maflcko closed this on Jul 25, 2023

  8. fanquake referenced this in commit c2ff87e1fa on Jul 26, 2023
  9. sidhujag referenced this in commit da984d973e on Aug 9, 2023
  10. Fabcien referenced this in commit 4a3ee7bd6a on Aug 21, 2023
  11. UdjinM6 referenced this in commit 5fdb05c4c2 on Nov 21, 2023
  12. PastaPastaPasta referenced this in commit 2d631df2e3 on Nov 26, 2023
  13. bitcoin locked this on Jul 24, 2024

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-24 06:14 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me