btcsignals: fix scoped_connection move-assignment and add default constructor #35120

pull thomasbuilds wants to merge 1 commits into bitcoin:master from thomasbuilds:fix-scoped-connection-move-assign changing 2 files +42 −1
  1. thomasbuilds commented at 12:07 PM on April 20, 2026: none

    Description:

    The defaulted move-assignment operator for scoped_connection overwrites m_conn without first calling disconnect(). The old callback remains registered in the signal and keeps firing — violating the RAII contract.

    Additionally, scoped_connection lacks a default constructor, making move-assignment impractical since there's no way to create an empty instance to later assign into (e.g. as a class member).

    This PR:

    • Replaces the defaulted move-assignment with a custom one that disconnects the old connection before moving.
    • Adds a default constructor, enabling the member-variable pattern:
      struct Wallet {
          btcsignals::scoped_connection m_conn;
          // m_conn can be move-assigned later
      };
      

    Reproduction (before fix):

    btcsignals::scoped_connection sc0 = sig.connect(IncrementCallback);
    btcsignals::scoped_connection sc1 = sig.connect(SquareCallback);
    sc0 = std::move(sc1);
    val = 3; sig(val);
    // Expected: 9 (only SquareCallback)
    // Actual:  16 (both callbacks fire, old connection leaked)
    

    Testing:

    Added scoped_connection_move_assignment test covering default construction, move-assignment disconnect behavior, and self-move-assignment safety. Test fails on master (16 != 9), passes with this change.

  2. DrahtBot commented at 12:07 PM on April 20, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #35043 (refactor: Properly return from ThreadSafeQuestion signal + btcsignals follow-ups by maflcko)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  3. sedited commented at 12:14 PM on April 20, 2026: contributor

    cc @theuni

  4. maflcko commented at 12:58 PM on April 20, 2026: member

    heh, I guess at least this on in particular is my fault due to suggesting it in #34495 (review).

    It is unused in the codebase right now, so there is no observable bug.

    I wonder what the use-case for move assignment is. I presume, back when I suggested to default this, I assumed that scoped_connection() exists to create an empty connection. However, it does not exist.

    I guess the use-case would be something like:

    struct Wallet{
      scoped_connection a{};
      scoped_connection b{};
    }
    

    Where those are later move-assigned? However, the constructor does not exist to create those, so I think it would make sense to either enable both or none.

    Moreover, if going strictly after https://www.boost.org/doc/libs/latest/doc/html/boost/signals2/scoped_connection.html#id-1_3_34_6_2_1_1_2_3_6-bb and https://www.boost.org/doc/libs/latest/doc/html/boost/signals2/connection.html#id-1_3_34_6_2_1_1_1_5-bb, it is also possible to re-seat a non-scoped connection, which is not fixed by this pull request either, so it seems incomplete.

    I don't know what to do here, I just wanted to mention that there seems to be context on the larger picture missing from the pull description.

  5. btcsignals: fix scoped_connection move-assignment and add default constructor
    The defaulted move-assignment operator for scoped_connection overwrites
    m_conn without first disconnecting the previously held connection. This
    causes the old callback to remain registered in the signal and continue
    firing -- violating the RAII contract of scoped_connection.
    
    Replace the defaulted operator with a custom one that calls disconnect()
    before moving.
    
    Also add a default constructor so that scoped_connection can be used as a
    class member that is move-assigned later, which is the primary use case
    for move-assignment.
    
    Add a regression test covering default construction, move-assignment
    disconnect behavior, and self-move-assignment safety.
    efeba209d8
  6. thomasbuilds force-pushed on Apr 20, 2026
  7. thomasbuilds commented at 1:11 PM on April 20, 2026: none

    Thanks for the context! Updated the PR to address your points

  8. maflcko commented at 1:15 PM on April 20, 2026: member

    I don't think they are addressed. It is still unclear to me what we want to achieve here. Do we want fully boost-compatibility, in which case this pull is still incomplete, as per my last comment. Do we want a subset? If yes, why that subset?

  9. thomasbuilds renamed this:
    btcsignals: disconnect old connection on scoped_connection move-assignment
    btcsignals: fix scoped_connection move-assignment and add default constructor
    on Apr 20, 2026
  10. thomasbuilds commented at 1:24 PM on April 20, 2026: none

    Ok my reasoning was the move-assignment is broken, and fixing it without a default constructor makes it useless (no way to create an empty scoped_connection to assign into), so I added both together as the minimal set to make it work.

    But you're right that this creates an arbitrary subset, it fixes one thing while leaving out operator=(connection&&) without a clear rationale for where to draw the line.

    So I see two paths

    1. Delete the broken move-assignment. It's unused, so removing it is safe. If a use case comes up later, the full set of operations can be added correctly at that point 2. Go for full boost-compatibility: fix move-assignment, add default constructor, add operator=(connection&&), etc

    What do you think?

  11. DrahtBot added the label CI failed on Apr 20, 2026
  12. maflcko commented at 1:57 PM on April 20, 2026: member

    operator=(connection&&) for re-seating from a raw connection (as in boost) is left out of scope — can be added separately if needed.

    Ah sorry, I was wrong to indicate this was missing. Whatever the underlying shared_ptr is doing is already correct here, IIUC. It is fine for you to use an LLM, but please still try to think critically. Otherwise, this may lead to more issues, where a misunderstanding is amplified/repeated instead of corrected.

  13. thomasbuilds commented at 2:08 PM on April 20, 2026: none

    yes you're right went too fast there

  14. DrahtBot removed the label CI failed on Apr 20, 2026
  15. maflcko approved
  16. maflcko commented at 3:17 PM on April 20, 2026: member

    seems fine. I guess the alternative would be to go back to =delete.

  17. maflcko added this to the milestone 32.0 on Apr 20, 2026

Milestone
32.0


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-21 09:12 UTC

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