From 7be902657d1a068b657700d9246d874238d14592 Mon Sep 17 00:00:00 2001 From: echennells Date: Sat, 4 Jul 2026 03:19:41 +0000 Subject: [PATCH] Fix get_data/get_client_filters livelock in tx and filter out protocols. handle_receive_get_data and handle_receive_get_filters drive the first send synchronously and then return false to drop the channel subscription. On a request that completes synchronously (a get_data carrying no transaction items, or a get_filters yielding an empty ancestry) the send reaches its terminal branch and resubscribes via SUBSCRIBE_CHANNEL while still inside the channel subscriber notification that invoked the handler. The channel subscriber is a std::list iterated in place; the re-entrant subscribe appends to that list, and the handler's false return advances the iterator into the appended element, re-invoking the handler on the same message indefinitely and pegging the channel strand (the author's flagged "BUGBUG: registration race"). Post the initial send so it runs on a later strand cycle, outside the subscriber notification. The subscription is still dropped on receipt, the terminal resubscribe still rides the completion of the last asynchronous send, and only the synchronous-completion path is deferred. --- src/protocols/protocol_filter_out_70015.cpp | 5 ++++- src/protocols/protocol_transaction_out_106.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/protocols/protocol_filter_out_70015.cpp b/src/protocols/protocol_filter_out_70015.cpp index 06348d85..1a9115e5 100644 --- a/src/protocols/protocol_filter_out_70015.cpp +++ b/src/protocols/protocol_filter_out_70015.cpp @@ -220,8 +220,11 @@ bool protocol_filter_out_70015::handle_receive_get_filters(const code& ec, return false; } + // Post send and desubscribe. Posting prevents a synchronous completion + // (an empty ancestry) from resubscribing re-entrantly within the channel + // subscriber notification. span(events::ancestry_msecs, start); - send_filter(error::success, ancestry); + POST(send_filter, error::success, ancestry); return false; } diff --git a/src/protocols/protocol_transaction_out_106.cpp b/src/protocols/protocol_transaction_out_106.cpp index a208307f..0ebcbf3f 100644 --- a/src/protocols/protocol_transaction_out_106.cpp +++ b/src/protocols/protocol_transaction_out_106.cpp @@ -150,8 +150,10 @@ bool protocol_transaction_out_106::handle_receive_get_data(const code& ec, if (stopped(ec)) return false; - // Send and desubscribe. - send_transaction(error::success, zero, message); + // Post send and desubscribe. Posting prevents a synchronous completion + // (a get_data with no transaction items) from resubscribing re-entrantly + // within the channel subscriber notification. + POST(send_transaction, error::success, zero, message); return false; } @@ -175,7 +177,6 @@ void protocol_transaction_out_106::send_transaction(const code& ec, if (message->items.at(index).is_transaction_type()) break; - // BUGBUG: registration race. if (index >= message->items.size()) { // Complete, resubscribe to transaction requests.